General Setup

General Setup


Create a new analysis directories.

- general directory

- stains directory

- for plots

- for output of summary results

- for baseline tables

- for genetic analyses

- for Cox regression results
source("scripts/functions.R")
source("scripts/pack02.packages.R")

* General packages...

* Genomic packages...
Today = format(as.Date(as.POSIXlt(Sys.time())), "%Y%m%d")
Today.Report = format(as.Date(as.POSIXlt(Sys.time())), "%A, %B %d, %Y")
source("scripts/colors.R")

Background

This notebook contains additional figures of the project.

Loading data

load(paste0(PROJECT_loc, "/",Today,".",PROJECTNAME,".bulkRNAseq.main_analysis.RData"))
# load(paste0(PROJECT_loc, "/20241017.",PROJECTNAME,".bulkRNAseq.main_analysis.RData"))

Fix some variables

We need to get the ‘conventional unit’ versions of cholesterols.

AERNASE.clin <- merge(AERNASE.clin.targets, 
                            subset(AEDB.CEA, select = c("STUDY_NUMBER", 
                                                        "risk614", 
                                                        "LDL_finalCU", "HDL_finalCU", "TC_finalCU", "TG_finalCU")), 
                            by.x = "study_number", by.y = "STUDY_NUMBER", sort = TRUE, all.x = TRUE)

Additional figures

Age and sex

We want to create per-age-group figures median ± interquartile range.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by sex.
  • Box and Whisker plot for CONVOCALS_downstream plaque levels by (sex and) age group (<55, 55-64, 65-74, 75-84, 85+).

# ?ggpubr::ggboxplot()
compare_means(HMOX1 ~ Gender,  data = AERNASE.clin, method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin, 
                  x = c("Gender"),
                  y = "HMOX1", 
                  xlab = "gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Gender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

library(dplyr)

AERNASE.clin <- AERNASE.clin %>% dplyr::mutate(AgeGroup = factor(case_when(Age < 55 ~ "<55",
                                                     Age >= 55  & Age <= 64 ~ "55-64",
                                                     Age >= 65  & Age <= 74 ~ "65-74",
                                                     Age >= 75  & Age <= 84 ~ "75-84",
                                                     Age >= 85 ~ "85+"))) 

AERNASE.clin <- AERNASE.clin %>% dplyr::mutate(AgeGroupSex = factor(case_when(Age < 55 & Gender == "male" ~ "<55 males" ,
                                                        Age >= 55  & Age <= 64 & Gender == "male"~ "55-64 males",
                                                        Age >= 65  & Age <= 74 & Gender == "male"~ "65-74 males",
                                                        Age >= 75  & Age <= 84 & Gender == "male"~ "75-84 males",
                                                        Age >= 85 & Gender == "male"~ "85+ males",
                                                        Age < 55 & Gender == "female" ~ "<55 females" ,
                                                        Age >= 55  & Age <= 64 & Gender == "female"~ "55-64 females ",
                                                        Age >= 65  & Age <= 74 & Gender == "female"~ "65-74 females",
                                                        Age >= 75  & Age <= 84 & Gender == "female"~ "75-84 females",
                                                        Age >= 85 & Gender == "female"~ "85+ females")))

table(AERNASE.clin$AgeGroup, AERNASE.clin$Gender)
       
        female male
  <55       16   38
  55-64     82  195
  65-74    114  318
  75-84     84  213
  85+       12   20
table(AERNASE.clin$AgeGroupSex)

   <55 females      <55 males 55-64 females     55-64 males  65-74 females    65-74 males  75-84 females    75-84 males    85+ females      85+ males 
            16             38             82            195            114            318             84            213             12             20 

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and age group as median ± interquartile range.


# ?ggpubr::ggboxplot()
compare_means(HMOX1 ~ AgeGroup,  data = AERNASE.clin, method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin, 
                  x = c("AgeGroup"),
                  y = "HMOX1", 
                  xlab = "Age groups (years)",
                  ylab = "HMOX1 (normalized expression)",
                  color = "AgeGroup",
                  palette = "npg",
                  # add = "median_iqr")
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = AgeGroup), label = "p.format", method = "kruskal.test")
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.AgeGroup.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ AgeGroup, group.by = "Gender", data = AERNASE.clin, method = "kruskal.test")
ggpubr::ggboxplot(AERNASE.clin, 
                  x = c("AgeGroup"),
                  y = "HMOX1", 
                  xlab = "Age groups (years) per gender",
                  ylab = "HMOX1 (normalized expression",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  # add = "median_iqr")
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.AgeGroup_perGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

Hypertension & blood pressure

We want to create figures of CONVOCALS_downstream levels stratified by hypertension/blood pressure, and use of anti-hypertensive drugs.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by hypertension group (no, yes)
  • Box and Whisker plot for CONVOCALS_downstream plaque levels by systolic blood pressure group (<120, 120-139, 140-159,160+)
library(dplyr)

AERNASE.clin <- AERNASE.clin %>% mutate(SBPGroup = factor(case_when(systolic < 120 ~ "<120",
                                                     systolic >= 120  & systolic <= 139 ~ "120-139",
                                                     systolic >= 140  & systolic <= 159 ~ "140-159",
                                                     systolic >= 160 ~ "160+"))) 

table(AERNASE.clin$SBPGroup, AERNASE.clin$Gender)
         
          female male
  <120        18   57
  120-139     63  138
  140-159     79  211
  160+       116  260

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and hypertension/blood pressure group as median ± interquartile range.

HMOX1

detach("package:EnsDb.Hsapiens.v86", unload = TRUE)
detach("package:ensembldb", unload = TRUE)
compare_means(HMOX1 ~ SBPGroup, data = AERNASE.clin %>% filter(!is.na(SBPGroup)), method = "kruskal.test")
filter: removed 150 rows (14%), 942 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(SBPGroup)), 
                  x = c("SBPGroup"),
                  y = "HMOX1", 
                  xlab = "Systolic blood pressure (mmHg)",
                  ylab = "HMOX1 (normalized expression)",
                  color = "SBPGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = SBPGroup), label = "p.format", method = "kruskal.test")
filter: removed 150 rows (14%), 942 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.SBPGroup.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypertension.selfreport, data = AERNASE.clin %>% filter(!is.na(Hypertension.selfreport)), method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypertension.selfreport)), 
                  x = c("Hypertension.selfreport"),
                  y = "HMOX1", 
                  xlab = "Self-reported hypertension",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Hypertension.selfreport",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.selfreport), label = "p.format", method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Hypertension.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypertension.drugs, data = AERNASE.clin %>% filter(!is.na(Hypertension.drugs)), method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypertension.drugs)), 
                  x = c("Hypertension.drugs"),
                  y = "HMOX1", 
                  xlab = "Hypertension medication use",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Hypertension.drugs",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.drugs), label = "p.format", method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.HypertensionDrugs.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ SBPGroup, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(SBPGroup)), method = "kruskal.test")
filter: removed 150 rows (14%), 942 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(SBPGroup)), 
                  x = c("SBPGroup"),
                  y = "HMOX1", 
                  xlab = "Systolic blood pressure (mmHg) per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 150 rows (14%), 942 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.SBPGroup_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypertension.selfreport, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(Hypertension.selfreport)), method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypertension.selfreport)), 
                  x = c("Hypertension.selfreport"),
                  y = "HMOX1", 
                  xlab = "Self-reported hypertension per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Hypertension_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypertension.drugs, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(Hypertension.drugs)), method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypertension.drugs)), 
                  x = c("Hypertension.drugs"),
                  y = "HMOX1", 
                  xlab = "Hypertension medication use per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Hypertension.drugs_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ SBPGroup, group.by = "Hypertension.drugs", data = AERNASE.clin %>% filter(!is.na(SBPGroup) & !is.na(Hypertension.drugs)), method = "kruskal.test")
filter: removed 151 rows (14%), 941 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(SBPGroup) & !is.na(Hypertension.drugs)), 
                  x = c("SBPGroup"),
                  y = "HMOX1", 
                  xlab = "Systolic blood pressure (mmHg) by medication use",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Hypertension.drugs",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.drugs), label = "p.format", method = "kruskal.test")
filter: removed 151 rows (14%), 941 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.SBPGroup_byHypertensionDrugs.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypertension.selfreport, group.by = "Hypertension.drugs", data = AERNASE.clin %>% filter(!is.na(Hypertension.selfreport) & !is.na(Hypertension.drugs)), method = "kruskal.test")
filter: removed 31 rows (3%), 1,061 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypertension.selfreport) & !is.na(Hypertension.drugs)), 
                  x = c("Hypertension.selfreport"),
                  y = "HMOX1", 
                  xlab = "Self-reported hypertension per medication use",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Hypertension.drugs",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Hypertension.drugs), label = "p.format", method = "kruskal.test")
filter: removed 31 rows (3%), 1,061 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Hypertension.selfreport_byHypertensionDrugs.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

Hypercholesterolemia & LDL levels

We want to create figures of CONVOCALS_downstream levels stratified by hypercholesterolemia/LDL-levels, and use of lipid-lowering drugs.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by hypercholesterolemia (risk614) group (no, yes)
  • Box and Whisker plot for CONVOCALS_downstream plaque levels by lipid-lowering drugs group (no, yes)
  • Box and Whisker plot for CONVOCALS_downstream plaque levels by LDL-levels (mmol/L) group (<100, 100-129, 130-159, 160-189, 190+)
library(dplyr)

AERNASE.clin <- AERNASE.clin %>% mutate(LDLGroup = factor(case_when(LDL_finalCU < 100 ~ "<100",
                                                     LDL_finalCU >= 100  & LDL_finalCU <= 129 ~ "100-129",
                                                     LDL_finalCU >= 130  & LDL_finalCU <= 159 ~ "130-159",
                                                     LDL_finalCU >= 160  & LDL_finalCU <= 189 ~ "160-189",
                                                     LDL_finalCU >= 190 ~ "190+"))) 


table(AERNASE.clin$LDLGroup, AERNASE.clin$Gender)
         
          female male
  <100        83  219
  100-129     49  120
  130-159     33   68
  160-189     16   24
  190+         9   14
require(sjlabelled)

AERNASE.clin$risk614 <- to_factor(AERNASE.clin$risk614)

# Fix plaquephenotypes
attach(AERNASE.clin)
AERNASE.clin[,"Hypercholesterolemia"] <- NA
AERNASE.clin$Hypercholesterolemia[risk614 == "missing value"] <- NA
AERNASE.clin$Hypercholesterolemia[risk614 == -999] <- NA
AERNASE.clin$Hypercholesterolemia[risk614 == 0] <- "no"
AERNASE.clin$Hypercholesterolemia[risk614 == 1] <- "yes"
detach(AERNASE.clin)

table(AERNASE.clin$risk614, AERNASE.clin$Hypercholesterolemia)
   
     no yes
  0 306   0
  1   0 697
# AEDB.temp <- subset(AEDB,  select = c("STUDY_NUMBER", "UPID", "Age", "Gender", "Hospital", "Artery_summary", "risk614", "Hypercholesterolemia"))
# require(labelled)
# AEDB.temp$Gender <- to_factor(AEDB.temp$Gender)
# AEDB.temp$Hospital <- to_factor(AEDB.temp$Hospital)
# AEDB.temp$Artery_summary <- to_factor(AEDB.temp$Artery_summary)
# 
# DT::datatable(AEDB.temp[1:10,], caption = "Excerpt of the whole AEDB.", rownames = FALSE)
# 
# rm(AEDB.temp)

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and hypercholesterolemia/LDL-levels group, as well as stratified by lipid-lowering drugs users as median ± interquartile range.

HMOX1


compare_means(HMOX1 ~ LDLGroup, data = AERNASE.clin %>% filter(!is.na(LDLGroup)), method = "kruskal.test")
filter: removed 457 rows (42%), 635 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(LDLGroup)), 
                  x = c("LDLGroup"),
                  y = "HMOX1", 
                  xlab = "LDL (mg/dL) per gender",
                  ylab = "HMOX1 (normalized expression))",
                  color = "LDLGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")
filter: removed 457 rows (42%), 635 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.LDLGroups.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ LDLGroup, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(LDLGroup)), method = "kruskal.test")
filter: removed 457 rows (42%), 635 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(LDLGroup)), 
                  x = c("LDLGroup"),
                  y = "HMOX1", 
                  xlab = "LDL (mg/dL) per gender",
                  ylab = "HMOX1 (normalized expression))",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 457 rows (42%), 635 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.LDLGroups_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypercholesterolemia, data = AERNASE.clin %>% filter(!is.na(Hypercholesterolemia)), method = "kruskal.test")
filter: removed 89 rows (8%), 1,003 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypercholesterolemia)), 
                  x = c("Hypercholesterolemia"),
                  y = "HMOX1", 
                  xlab = "Diagnosed hypercholesterolemia",
                  ylab = "HMOX1 (normalized expression))",
                  color = "Hypercholesterolemia",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")
filter: removed 89 rows (8%), 1,003 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Hypercholesterolemia.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypercholesterolemia, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(Hypercholesterolemia)), method = "kruskal.test")
filter: removed 89 rows (8%), 1,003 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypercholesterolemia)), 
                  x = c("Hypercholesterolemia"),
                  y = "HMOX1", 
                  xlab = "Diagnosed hypercholesterolemia per gender",
                  ylab = "HMOX1 (normalized expression))",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 89 rows (8%), 1,003 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Hypercholesterolemia_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Med.Statin.LLD, data = AERNASE.clin %>% filter(!is.na(Med.Statin.LLD)), method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Med.Statin.LLD)), 
                  x = c("Med.Statin.LLD"),
                  y = "HMOX1", 
                  xlab = "Lipid-lowering drug use",
                  ylab = "HMOX1 (normalized expression))",
                  color = "Med.Statin.LLD",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Med.Statin.LLD.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Med.Statin.LLD, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(Med.Statin.LLD)), method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Med.Statin.LLD)), 
                  x = c("Med.Statin.LLD"),
                  y = "HMOX1", 
                  xlab = "Lipid-lowering drug use per gender",
                  ylab = "HMOX1 (normalized expression))",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed one row (<1%), 1,091 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Med.Statin.LLD_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ LDLGroup, group.by = "Med.Statin.LLD", data = AERNASE.clin %>% filter(!is.na(LDLGroup) & !is.na(Med.Statin.LLD)), method = "kruskal.test")
filter: removed 458 rows (42%), 634 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(LDLGroup) & !is.na(Med.Statin.LLD)), 
                  x = c("LDLGroup"),
                  y = "HMOX1", 
                  xlab = "LDL (mg/dL) per LLD use",
                  ylab = "HMOX1 (normalized expression))",
                  color = "Med.Statin.LLD",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Med.Statin.LLD), label = "p.format", method = "kruskal.test")
filter: removed 458 rows (42%), 634 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.LDLGroups_byMed.Statin.LLD.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ Hypercholesterolemia, group.by = "Med.Statin.LLD", data = AERNASE.clin %>% filter(!is.na(Hypercholesterolemia) & !is.na(Med.Statin.LLD)), method = "kruskal.test")
filter: removed 90 rows (8%), 1,002 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(Hypercholesterolemia) & !is.na(Med.Statin.LLD)), 
                  x = c("Hypercholesterolemia"),
                  y = "HMOX1", 
                  xlab = "Diagnosed hypercholesterolemia per LLD use",
                  ylab = "HMOX1 (normalized expression))",
                  color = "Med.Statin.LLD",
                  palette = c("#49A01D", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Med.Statin.LLD), label = "p.format", method = "kruskal.test")
filter: removed 90 rows (8%), 1,002 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.LDLGroups_byMed.Statin.LLD.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

Kidney function (eGFR)

We want to create figures of CONVOCALS_downstream levels stratified by kidney function.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by chronic kidney disease (CKD) group (1, 2, 3, 4, 5)
  • Box and Whisker plot for CONVOCALS_downstream plaque levels by eGFR (MDRD-based) group (90+, 60-89, 30-59, <30)
library(dplyr)

AERNASE.clin <- AERNASE.clin %>% mutate(eGFRGroup = factor(case_when(GFR_MDRD < 15 ~ "<15",
                                                             GFR_MDRD >= 15  & GFR_MDRD <= 29 ~ "15-29",
                                                             GFR_MDRD >= 30  & GFR_MDRD <= 59 ~ "30-59",
                                                             GFR_MDRD >= 60  & GFR_MDRD <= 89 ~ "60-89",
                                                             GFR_MDRD >= 90 ~ "90+")))

table(AERNASE.clin$eGFRGroup, AERNASE.clin$Gender)
       
        female male
  <15        1    0
  15-29      3   11
  30-59     77  165
  60-89    165  409
  90+       47  155
table(AERNASE.clin$eGFRGroup, AERNASE.clin$KDOQI)
       
        No data available/missing Normal kidney function CKD 2 (Mild) CKD 3 (Moderate) CKD 4 (Severe) CKD 5 (Failure)
  <15                           0                      0            0                0              0               1
  15-29                         0                      0            0                0             14               0
  30-59                         0                      0            0              242              0               0
  60-89                         0                      0          574                0              0               0
  90+                           0                    202            0                0              0               0

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and kidney function group as median ± interquartile range.

HMOX1


# Global test

compare_means(HMOX1 ~ eGFRGroup, data = AERNASE.clin %>% filter(!is.na(eGFRGroup)), method = "kruskal.test")
filter: removed 59 rows (5%), 1,033 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(eGFRGroup)), 
                  x = c("eGFRGroup"),
                  y = "HMOX1", 
                  xlab = "eGFR (mL/min per 1.73 m2)",
                  ylab = "HMOX1 (normalized expression)",
                  color = "eGFRGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(method = "kruskal.test")
filter: removed 59 rows (5%), 1,033 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.EGFR.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ eGFRGroup, group.by = "Gender",  data = AERNASE.clin %>% filter(!is.na(eGFRGroup)), method = "kruskal.test")
filter: removed 59 rows (5%), 1,033 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(eGFRGroup)), 
                  x = c("eGFRGroup"),
                  y = "HMOX1", 
                  xlab = "eGFR (mL/min per 1.73 m2) per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 59 rows (5%), 1,033 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.EGFR_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ KDOQI, data = AERNASE.clin %>% filter(!is.na(KDOQI)), method = "kruskal.test")
filter: removed 28 rows (3%), 1,064 rows remaining
p1 <- ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(KDOQI)), 
                  x = c("KDOQI"),
                  y = "HMOX1", 
                  xlab = "Kidney function (KDOQI)",
                  ylab = "HMOX1 (normalized expression)",
                  color = "KDOQI",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(aes(group = KDOQI), label = "p.format", method = "kruskal.test")
filter: removed 28 rows (3%), 1,064 rows remaining
ggpar(p1 + rotate_x_text(45), legend = "right") 
rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.KDOQI.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ KDOQI, group.by = "Gender",   data = AERNASE.clin %>% filter(!is.na(KDOQI)), method = "kruskal.test")
filter: removed 28 rows (3%), 1,064 rows remaining
p1 <- ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(KDOQI)), 
                  x = c("KDOQI"),
                  y = "HMOX1", 
                  xlab = "Kidney function (KDOQI) per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 28 rows (3%), 1,064 rows remaining
ggpar(p1 + rotate_x_text(45), legend = "right") 
rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.KDOQI_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ eGFRGroup,  data = AERNASE.clin %>% filter(!is.na(eGFRGroup) & !is.na(KDOQI)), method = "kruskal.test")
filter: removed 59 rows (5%), 1,033 rows remaining
p1 <- ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(eGFRGroup) & !is.na(KDOQI)), 
                  x = c("eGFRGroup"),
                  y = "HMOX1", 
                  xlab = "eGFR (mL/min per 1.73 m2) by KDOQI group",
                  ylab = "HMOX1 (normalized expression)",
                  color = "KDOQI",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(method = "kruskal.test")
filter: removed 59 rows (5%), 1,033 rows remaining
ggpar(p1, legend = "right")
rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.EGFR_KDOQI.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

BMI

We want to create figures of CONVOCALS_downstream levels stratified by BMI.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by BMI WHO group (underweight, normal, overweight, obese)
  • Box and Whisker plot for CONVOCALS_downstream plaque levels by BMI group (<18.5, 18.5-24.9, 25, 29.9, 30-24.9, 35+)
library(dplyr)

AERNASE.clin <- AERNASE.clin %>% mutate(BMIGroup = factor(case_when(BMI < 18.5 ~ "<18.5",
                                                     BMI >= 18.5  & BMI < 25 ~ "18.5-24",
                                                     BMI >= 25  & BMI < 30 ~ "25-29",
                                                     BMI >= 30  & BMI < 35 ~ "30-35",
                                                     BMI >= 35 ~ "35+"))) 

# require(labelled)
# AERNASE.clin$BMI_US <- as_factor(AERNASE.clin$BMI_US)
# AERNASE.clin$BMI_WHO <- as_factor(AERNASE.clin$BMI_WHO)
# table(AERNASE.clin$BMI_WHO, AERNASE.clin$BMI_US)

table(AERNASE.clin$BMIGroup, AERNASE.clin$Gender)
         
          female male
  <18.5        6    4
  18.5-24    118  273
  25-29      120  373
  30-35       37   89
  35+         13   17
table(AERNASE.clin$BMIGroup, AERNASE.clin$BMI_WHO)
         
          No data available/missing Underweight Normal Overweight Obese
  <18.5                           0           9      0          0     0
  18.5-24                         0           0    391          0     0
  25-29                           0           0      0        492     0
  30-35                           0           0      0          0   126
  35+                             0           0      0          0    30

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and age group as median ± interquartile range.

HMOX1


# Global test
compare_means(HMOX1 ~ BMIGroup,  data = AERNASE.clin %>% filter(!is.na(BMIGroup)), method = "kruskal.test")
filter: removed 42 rows (4%), 1,050 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(BMIGroup)), 
                  x = c("BMIGroup"),
                  y = "HMOX1", 
                  xlab = "BMI groups (kg/m2)",
                  ylab = "HMOX1 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "BMIGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")
filter: removed 42 rows (4%), 1,050 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.BMI.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ BMIGroup, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(BMIGroup)), method = "kruskal.test")
filter: removed 42 rows (4%), 1,050 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(BMIGroup)), 
                  x = c("BMIGroup"),
                  y = "HMOX1", 
                  xlab = "BMI groups (kg/m2) per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 42 rows (4%), 1,050 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.BMI_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ BMIGroup,  data = AERNASE.clin %>% filter(!is.na(BMIGroup) & !is.na(BMI_WHO)), method = "kruskal.test")
filter: removed 44 rows (4%), 1,048 rows remaining
p1 <- ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(BMIGroup) & !is.na(BMI_WHO)), 
                  x = c("BMIGroup"),
                  y = "HMOX1", 
                  xlab = "BMI groups (kg/m2) per WHO categories",
                  ylab = "HMOX1 (normalized expression)",
                  color = "BMI_WHO",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(method = "kruskal.test")
filter: removed 44 rows (4%), 1,048 rows remaining
ggpar(p1, legend = "right")
rm(p1)
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.BMI_byWHO.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

Diabetes

We want to create figures of CONVOCALS_downstream levels stratified by type 2 diabetes.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by type 2 diabetes group (no, yes)

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and age group as median ± interquartile range.

HMOX1


compare_means(HMOX1 ~ DiabetesStatus,  
              data = AERNASE.clin %>% filter(!is.na(DiabetesStatus)), method = "kruskal.test")
filter: no rows removed
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(DiabetesStatus)),
                  x = c("DiabetesStatus"),
                  y = "HMOX1",
                  xlab = "Diabetes status",
                  ylab = "HMOX1 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "DiabetesStatus",
                  palette = "npg",
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(label = "p.format", method = "kruskal.test")
filter: no rows removed
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Diabetes.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ DiabetesStatus, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(DiabetesStatus)), method = "kruskal.test")
filter: no rows removed
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(DiabetesStatus)),
                  x = c("DiabetesStatus"),
                  y = "HMOX1",
                  xlab = "Diabetes status per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: no rows removed
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Diabetes_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

NA
NA

Smoking

We want to create figures of CONVOCALS_downstream levels stratified by smoking.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by smoking group (never, ex, current)

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and age group as median ± interquartile range.

HMOX1


# Global test
compare_means(HMOX1 ~ SmokerStatus,  data = AERNASE.clin %>% filter(!is.na(SmokerStatus)), method = "kruskal.test")
filter: removed 45 rows (4%), 1,047 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(SmokerStatus)), 
                  x = c("SmokerStatus"),
                  y = "HMOX1", 
                  xlab = "Smoker status",
                  ylab = "HMOX1 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "SmokerStatus",
                  palette = "npg",
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(label = "p.format", method = "kruskal.test")
filter: removed 45 rows (4%), 1,047 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Smoking.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ SmokerStatus, group.by ="Gender", data = AERNASE.clin %>% filter(!is.na(SmokerStatus)), method = "kruskal.test")
filter: removed 45 rows (4%), 1,047 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(SmokerStatus)), 
                  x = c("SmokerStatus"),
                  y = "HMOX1", 
                  xlab = "Smoker status per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = c("median_iqr", "jitter")) +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 45 rows (4%), 1,047 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Smoking_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

Stenosis

We want to create figures of CONVOCALS_downstream levels stratified by stenosis grade.

  • Box and Whisker plot for CONVOCALS_downstream plaque levels by stenosis grade group (<70, 70-89, 90+)
library(dplyr)

AERNASE.clin <- AERNASE.clin %>% mutate(StenoticGroup = factor(case_when(stenose == "0-49%" ~ "<70",
                                                     stenose == "0-49%" ~ "<70",
                                                     stenose == "50-70%" ~ "<70",
                                                     stenose == "70-90%" ~ "70-89",
                                                     stenose == "50-99%" ~ "90+",
                                                     stenose == "70-99%" ~ "90+",
                                                     stenose == "100% (Occlusion)" ~ "90+",
                                                     stenose == "90-99%" ~ "90+")))

table(AERNASE.clin$StenoticGroup, AERNASE.clin$Gender)
       
        female male
  <70       17   61
  70-89    151  358
  90+      133  342
table(AERNASE.clin$stenose, AERNASE.clin$StenoticGroup)
                  
                   <70 70-89 90+
  missing            0     0   0
  0-49%              5     0   0
  50-70%            73     0   0
  70-90%             0   509   0
  90-99%             0     0 438
  100% (Occlusion)   0     0  10
  NA                 0     0   0
  50-99%             0     0   4
  70-99%             0     0  23
  99                 0     0   0

Now we can draw some graphs of plaque CONVOCALS_downstream levels per sex and age group as median ± interquartile range.

HMOX1


# Global test
compare_means(HMOX1 ~ StenoticGroup,  data = AERNASE.clin %>% filter(!is.na(StenoticGroup)), method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(StenoticGroup)), 
                  x = c("StenoticGroup"),
                  y = "HMOX1", 
                  xlab = "Stenotic grade",
                  ylab = "HMOX1 (normalized expression)",
                  # color = "Gender",
                  # palette = c("#D5267B", "#1290D9"),
                  color = "StenoticGroup",
                  palette = "npg",
                  add = "jitter") +
  stat_compare_means(label = "p.format", method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Stenosis.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

compare_means(HMOX1 ~ StenoticGroup, group.by = "Gender", data = AERNASE.clin %>% filter(!is.na(StenoticGroup)), method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggpubr::ggboxplot(AERNASE.clin %>% filter(!is.na(StenoticGroup)), 
                  x = c("StenoticGroup"),
                  y = "HMOX1", 
                  xlab = "Stenotic grade per gender",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "jitter") +
  stat_compare_means(aes(group = Gender), label = "p.format", method = "kruskal.test")
filter: removed 30 rows (3%), 1,062 rows remaining
ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.Stenosis_byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

Symptoms

We want to create per-symptom figures.

library(dplyr)

table(AERNASE.clin$AgeGroup, AERNASE.clin$AsymptSympt2G)
       
        Asymptomatic Symptomatic
  <55             10          44
  55-64           32         245
  65-74           47         385
  75-84           20         277
  85+              2          30
table(AERNASE.clin$Gender, AERNASE.clin$AsymptSympt2G)
        
         Asymptomatic Symptomatic
  female           24         284
  male             87         697
table(AERNASE.clin$AsymptSympt2G)

Asymptomatic  Symptomatic 
         111          981 

Now we can draw some graphs of plaque CONVOCALS_downstream levels per symptom group as median ± interquartile range.

HMOX1


# ?ggpubr::ggboxplot()
my_comparisons <- list(c("Asymptomatic", "Symptomatic"))

p1 <- ggpubr::ggboxplot(AERNASE.clin, 
                  x = "AsymptSympt2G", y = "HMOX1",
                  title = "HMOX1 (normalized expression) levels per symptom", 
                  xlab = "Symptoms",
                  ylab = "HMOX1 (normalized expression)",
                  color = "AsymptSympt2G", 
                  # palette = c(uithof_color[16], uithof_color[23]),
                  palette = "npg",
                  add = "dotplot", # Add dotplot
                  add.params = list(binwidth = 0.1, dotsize = 0.3)
          ) +
  stat_compare_means(comparisons = my_comparisons, method = "wilcox.test")
ggpar(p1, legend = c("right"), legend.title = "Symptoms")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.AsymptSympt2G.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

rm(p1)

compare_means(HMOX1 ~ AsymptSympt2G, group.by = "Gender", data = AERNASE.clin, method = "kruskal.test")
p1 <- ggpubr::ggboxplot(AERNASE.clin, 
                  x = "AsymptSympt2G", y = "HMOX1",
                  title = "HMOX1 (normalized expression) levels per symptom by gender", 
                  xlab = "Symptoms",
                  ylab = "HMOX1 (normalized expression)",
                  color = "Gender",
                  palette = c("#D5267B", "#1290D9"),
                  add = "dotplot", # Add dotplot
                  add.params = list(binwidth = 0.1, dotsize = 0.3)
          ) +
  stat_compare_means(aes(group = Gender), label = "p.format",  method = "wilcox.test")
ggpar(p1, legend = c("right"), legend.title = "Symptoms")

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.AsymptSympt2G.byGender.pdf"), plot = last_plot())
Saving 7.29 x 4.51 in image

rm(p1)

Forest plots

We would also like to visualize the multivariable analyses results.

library(ggplot2)
library(openxlsx)
model1_target <- read.xlsx(paste0(OUT_loc, "/", Today, ".AERNASE.clin.targets.Bin.Uni.",TRAIT_OF_INTEREST,".RANK.Symptoms.MODEL1.xlsx"))
model2_target <- read.xlsx(paste0(OUT_loc, "/", Today, ".AERNASE.clin.targets.Bin.Multi.",TRAIT_OF_INTEREST,".RANK.Symptoms.MODEL2.xlsx"))
model1_target$model <- "univariate"
model2_target$model <- "multivariate"

models_target <- rbind(model1_target, model2_target)
models_target
NA

Forest plots.

HMOX1

dat <- data.frame(group = factor(c("Age, sex-adjusted", "Age, sex, and adjusted for risk factors"), 
                           
                           levels=c("Age, sex, and adjusted for risk factors", "Age, sex-adjusted")),
                  cen = c(models_target$OR[models_target$Predictor=="HMOX1"]),
                  low = c(models_target$low95CI[models_target$Predictor=="HMOX1"]),
                  high = c(models_target$up95CI[models_target$Predictor=="HMOX1"]))

fp <- ggplot(data = dat, aes(x = group, y = cen, ymin = low, ymax = high)) +
  geom_pointrange(linetype = 2, size = 1, colour = c("#1290D9", "#49A01D")) + 
  geom_hline(yintercept = 1, lty = 2) +  # add a dotted line at x=1 after flip
  coord_flip(ylim = c(0.8, 1.7)) +  # flip coordinates (puts labels on y axis)
  xlab("Model") + ylab("OR (95% CI) for symptomatic plaques") +
  ggtitle("Plaque HMOX1 normalized expression (1 SD increment, n = 622)") +
  theme_minimal()  # use a white background
print(fp)

ggsave(file = paste0(PLOT_loc, "/",Today,".AERNASE.clin.",TRAIT_OF_INTEREST,".HMOX1.plaque.forest.pdf"), plot = fp)
Saving 7.29 x 4.51 in image

rm(fp)

Target expression vs. cytokines plaque levels correlations

We will plot the correlations of other cytokine plaque levels to the CONVOCALS_downstream plaque levels. These include:

  • IL2
  • IL4
  • IL5
  • IL6
  • IL8
  • IL9
  • IL10
  • IL12
  • IL13
  • IL21
  • INFG
  • TNFA
  • MIF
  • MCP1
  • MIP1a
  • RANTES
  • MIG
  • IP10
  • Eotaxin1
  • TARC
  • PARC
  • MDC
  • OPG
  • sICAM1
  • VEGFA
  • TGFB

In addition we will look at three metalloproteinases which were measured using an activity assay.

  • MMP2
  • MMP8
  • MMP9

The proteins were measured using FACS and LUMINEX. Given the different platforms used (FACS vs. LUMINEX), we will inverse rank-normalize these variables as well to scale them to the same scale as the CONVOCALS_downstream` plaque levels.

We will set the measurements that yielded ‘0’ to NA, as it is unlikely that any protein ever has exactly 0 copies. The ‘0’ yielded during the experiment are due to the limits of the detection.

Prepare data

# fix names
names(AEDB.CEA)[names(AEDB.CEA) == "VEFGA"] <- "VEGFA"

# fix names
names(AERNASE.clin)[names(AERNASE.clin) == "IL6"] <- "IL6rna"
names(AERNASE.clin)[names(AERNASE.clin) == "MMP9"] <- "MMP9rna"

cytokines <- c("IL2", "IL4", "IL5", "IL6", "IL8", "IL9", "IL10", "IL12", "IL13", "IL21", 
               "INFG", "TNFA", "MIF", "MCP1", "MIP1a", "RANTES", "MIG", "IP10", "Eotaxin1", 
               "TARC", "PARC", "MDC", "OPG", "sICAM1", "VEGFA", "TGFB")
metalloproteinases <- c("MMP2", "MMP8", "MMP9")


AERNASE.clin <- merge(AERNASE.clin, 
                            subset(AEDB.CEA, select = c("STUDY_NUMBER", 
                                                        cytokines,
                                                        metalloproteinases)), 
                            by.x = "study_number", by.y = "STUDY_NUMBER", sort = TRUE, all.x = TRUE)

proteins_of_interest <- c(cytokines, metalloproteinases)

proteins_of_interest_rank = unlist(lapply(proteins_of_interest, paste0, "_rank"))

# make variables numerics()
AERNASE.clin <- AERNASE.clin %>%
  mutate_each(funs(as.numeric), proteins_of_interest)
Warning: `funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas: 

  # Simple named list: 
  list(mean = mean, median = median)

  # Auto named with `tibble::lst()`: 
  tibble::lst(mean, median)

  # Using lambdas
  list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))Warning: `mutate_each()` was deprecated in dplyr 0.7.0.
Please use `across()` instead.
Error in `mutate_each_impl()`:
! Can't select columns that don't exist.
✖ Columns `IL2`, `IL4`, `IL5`, `IL8`, `IL9`, etc. don't exist.
Backtrace:
 1. AERNASE.clin %>% ...
 2. dplyr::mutate_each(., funs(as.numeric), proteins_of_interest)
 3. dplyr:::mutate_each_impl(tbl, funs, enquos(...), "mutate_each")

Visualize transformations

We will just visualize these transformations.

Correlations

Here we calculate correlations between CONVOCALS_downstream and 28 other cytokines. We use Spearman’s test, thus, correlations a given in rho. Please note the indications of measurement methods:

  • L: LUMINEX
  • E: ELISA
  • a: activity assay

While visually attractive we are not necessarily interested in the correlations between all the cytokines, rather of CONVOCALS_downstream` with other cytokines only.

HMOX1

Another version - probably not good.

Target expression vs. cytokines plaque levels lm()

Model 1

In this model we correct for Age, Gender, and year of surgery.

Here we use the inverse-rank normalized data - visually this is more normally distributed.

Analysis of plaque cytokines traits as a function of plaque CONVOCALS_downstream levels.

Model 2

In this model we correct for Age, Gender, year of surgery, Hypertension status, Diabetes status, current smoker status, lipid-lowering drugs (LLDs), antiplatelet medication, eGFR (MDRD), BMI, MedHx_CVD (combination of CAD history, stroke history, and peripheral interventions), and stenosis.

Here we use the inverse-rank normalized data - visually this is more normally distributed.

Analysis of plaque cytokines as a function of plaque CONVOCALS_downstream levels.

DT::datatable(GLM.results)

# Save the data
cat("Writing results to Excel-file...\n")
### Univariate
library(openxlsx)
write.xlsx(GLM.results,
           file = paste0(OUT_loc, "/",Today,".AERNASE.clin.Con.Multi.",TRAIT_OF_INTEREST,"_Plaque.Cytokines_Plaques.RANK.MODEL2.xlsx"),
           rowNames = FALSE, colNames = TRUE, sheetName = "Con.Multi.PlaquePheno")
# Removing intermediates
cat("Removing intermediate files...\n")
rm(TRAIT, trait, currentDF, GLM.results, GLM.results.TEMP, fit, model_step)

Target expression vs. vulnerability index

Here we plot the levels of inverse-rank normal transformed CONVOCALS_downstream plaque levels from experiment 1 and 2 to the Plaque vulnerability index.

Visualisations

HMOX1

Model 1

In this model we correct for Age, Gender, and year of surgery.

Here we use the inverse-rank normalized data - visually this is more normally distributed.

Analysis of the plaque vulnerability index as a function of plaque CONVOCALS_downstream levels.

Model 2

In this model we correct for Age, Gender, Hypertension status, Diabetes status, current smoker status, lipid-lowering drugs (LLDs), antiplatelet medication, eGFR (MDRD), BMI, MedHx_CVD (combination of CAD history, stroke history, and peripheral interventions), and stenosis..

Session information


Version:      v1.1.1
Last update:  2024-01-09
Written by:   Sander W. van der Laan (s.w.vanderlaan-2[at]umcutrecht.nl).
Description:  Script to analyse Targets from the Ather-Express Biobank Study.
Minimum requirements: R version 3.5.2 (2018-12-20) -- 'Eggshell Igloo', macOS Mojave (10.14.2).

**MoSCoW To-Do List**
The things we Must, Should, Could, and Would have given the time we have.
_M_

_S_

_C_

_W_

**Changes log**
* v1.1.1 Textual fixes.
* v1.1.0 Update to study database; update to bulk RNAseq data (deeper sequenced).
* v1.0.1 Fix to the start of this notebook.
* v1.0.0 Inital version.

Saving environment

save.image(paste0(PROJECT_loc, "/",Today,".",PROJECTNAME,".bulkRNAseq.additional_figures.RData"))
© 1979-2024 Sander W. van der Laan | s.w.vanderlaan[at]gmail[dot]com | vanderlaanand.science.
LS0tCnRpdGxlOiAiQWRkaXRpb25hbCBGaWd1cmVzIgpzdWJ0aXRsZTogQWNjb21wYW55aW5nICdDT05WT0NBTFNfZG93bnN0cmVhbScKYXV0aG9yOiAnW1NhbmRlciBXLiB2YW4gZGVyIExhYW4sIFBoRF0oaHR0cHM6Ly92YW5kZXJsYWFuYW5kLnNjaWVuY2UpIHwgcy53LnZhbmRlcmxhYW5bYXRdZ21haWxbZG90XWNvbScKZGF0ZTogJ2ByIFN5cy5EYXRlKClgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogCiAgICBjYWNoZTogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvbGxhcHNlOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogICAgZmlnLmFsaWduOiBjZW50ZXIKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGZpZ19oZWlnaHQ6IDEwCiAgICBmaWdfcmV0aW5hOiAyCiAgICBmaWdfd2lkdGg6IDEyCiAgICB0aGVtZTogcGFwZXIKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICBoaWdobGlnaHQ6IHRhbmdvCm1haW5mb250OiBIZWx2ZXRpY2EKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliCmtuaXQ6IHdvcmNzOjpjaXRlX2FsbAotLS0KCiMgR2VuZXJhbCBTZXR1cAoKIyBHZW5lcmFsIFNldHVwCmBgYHtyIGVjaG8gPSBGQUxTRX0Kcm0obGlzdCA9IGxzKCkpCmBgYAoKYGBge3IgTG9jYWxTeXN0ZW0sIGVjaG8gPSBGQUxTRX0Kc291cmNlKCJzY3JpcHRzL2xvY2FsLnN5c3RlbS5SIikKYGBgCgpgYGB7ciBTb3VyY2UgZnVuY3Rpb25zfQpzb3VyY2UoInNjcmlwdHMvZnVuY3Rpb25zLlIiKQpgYGAKCmBgYHtyIGxvYWRpbmdfcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNvdXJjZSgic2NyaXB0cy9wYWNrMDIucGFja2FnZXMuUiIpCmBgYAoKYGBge3IgU2V0dGluZzogQ29sb3JzfQpUb2RheSA9IGZvcm1hdChhcy5EYXRlKGFzLlBPU0lYbHQoU3lzLnRpbWUoKSkpLCAiJVklbSVkIikKVG9kYXkuUmVwb3J0ID0gZm9ybWF0KGFzLkRhdGUoYXMuUE9TSVhsdChTeXMudGltZSgpKSksICIlQSwgJUIgJWQsICVZIikKc291cmNlKCJzY3JpcHRzL2NvbG9ycy5SIikKYGBgCgpgYGB7ciBzZXR1cF9ub3RlYm9vaywgaW5jbHVkZT1GQUxTRX0KIyBXZSByZWNvbW1lbmQgdGhhdCB5b3UgcHJlcGFyZSB5b3VyIHJhdyBkYXRhIGZvciBhbmFseXNpcyBpbiAncHJlcGFyZV9kYXRhLlInLAojIGFuZCBlbmQgdGhhdCBmaWxlIHdpdGggZWl0aGVyIG9wZW5fZGF0YSh5b3VyZGF0YSksIG9yIGNsb3NlZF9kYXRhKHlvdXJkYXRhKS4KIyBUaGVuLCB1bmNvbW1lbnQgdGhlIGxpbmUgYmVsb3cgdG8gbG9hZCB0aGUgb3JpZ2luYWwgb3Igc3ludGhldGljIGRhdGEKIyAod2hpY2hldmVyIGlzIGF2YWlsYWJsZSksIHRvIGFsbG93IGFueW9uZSB0byByZXByb2R1Y2UgeW91ciBjb2RlOgojIGxvYWRfZGF0YSgpCgojIGZ1cnRoZXIgZGVmaW5lIHNvbWUga25pdHItb3B0aW9ucy4Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOCwgZmlnLnBhdGggPSAnRmlndXJlcy8nLCAKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBUUlVFLCAjIHNob3cgd2FybmluZ3MgZHVyaW5nIGNvZGVib29rIGdlbmVyYXRpb24KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBUUlVFLCAjIHNob3cgbWVzc2FnZXMgZHVyaW5nIGNvZGVib29rIGdlbmVyYXRpb24KICAgICAgICAgICAgICAgICAgICAgIGVycm9yID0gVFJVRSwgIyBkbyBub3QgaW50ZXJydXB0IGNvZGVib29rIGdlbmVyYXRpb24gaW4gY2FzZSBvZiBlcnJvcnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVzdWFsbHkgYmV0dGVyIGZvciBkZWJ1Z2dpbmcKICAgICAgICAgICAgICAgICAgICAgIGVjaG8gPSBUUlVFLCAgIyBzaG93IFIgY29kZQogICAgICAgICAgICAgICAgICAgICAgZXZhbCA9IFRSVUUpCgpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCgpKQojIHBhbmRlcjo6cGFuZGVyT3B0aW9ucygidGFibGUuc3BsaXQudGFibGUiLCBJbmYpCmxpYnJhcnkoIndvcmNzIikKbGlicmFyeSgicm1hcmtkb3duIikKYGBgCgoKIyBCYWNrZ3JvdW5kCgpUaGlzIG5vdGVib29rIGNvbnRhaW5zIGFkZGl0aW9uYWwgZmlndXJlcyBvZiB0aGUgcHJvamVjdC4KCgojIExvYWRpbmcgZGF0YQoKYGBge3IgTG9hZGluZyBwcm9qZWN0IGRhdGF9CmxvYWQocGFzdGUwKFBST0pFQ1RfbG9jLCAiLyIsVG9kYXksIi4iLFBST0pFQ1ROQU1FLCIuYnVsa1JOQXNlcS5tYWluX2FuYWx5c2lzLlJEYXRhIikpCiMgbG9hZChwYXN0ZTAoUFJPSkVDVF9sb2MsICIvMjAyNDEwMTcuIixQUk9KRUNUTkFNRSwiLmJ1bGtSTkFzZXEubWFpbl9hbmFseXNpcy5SRGF0YSIpKQpgYGAKCgojIEZpeCBzb21lIHZhcmlhYmxlcwoKV2UgbmVlZCB0byBnZXQgdGhlICdjb252ZW50aW9uYWwgdW5pdCcgdmVyc2lvbnMgb2YgY2hvbGVzdGVyb2xzLgoKYGBge3J9CkFFUk5BU0UuY2xpbiA8LSBtZXJnZShBRVJOQVNFLmNsaW4udGFyZ2V0cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQoQUVEQi5DRUEsIHNlbGVjdCA9IGMoIlNUVURZX05VTUJFUiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyaXNrNjE0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxETF9maW5hbENVIiwgIkhETF9maW5hbENVIiwgIlRDX2ZpbmFsQ1UiLCAiVEdfZmluYWxDVSIpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBieS54ID0gInN0dWR5X251bWJlciIsIGJ5LnkgPSAiU1RVRFlfTlVNQkVSIiwgc29ydCA9IFRSVUUsIGFsbC54ID0gVFJVRSkKYGBgCgoKIyBBZGRpdGlvbmFsIGZpZ3VyZXMKCiMjIEFnZSBhbmQgc2V4CldlIHdhbnQgdG8gY3JlYXRlIHBlci1hZ2UtZ3JvdXAgZmlndXJlcyBtZWRpYW4gwrEgaW50ZXJxdWFydGlsZSByYW5nZS4gCgotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciBgciBUUkFJVF9PRl9JTlRFUkVTVGAgcGxhcXVlIGxldmVscyBieSBzZXguCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIGByIFRSQUlUX09GX0lOVEVSRVNUYCBwbGFxdWUgbGV2ZWxzIGJ5IChzZXggYW5kKSBhZ2UgZ3JvdXAgKDw1NSwgNTUtNjQsIDY1LTc0LCA3NS04NCwgODUrKS4KCgpgYGB7ciBwZXIgU2V4fQoKIyA/Z2dwdWJyOjpnZ2JveHBsb3QoKQpjb21wYXJlX21lYW5zKEhNT1gxIH4gR2VuZGVyLCAgZGF0YSA9IEFFUk5BU0UuY2xpbiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJHZW5kZXIiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gImdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5HZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCgpgYGAKCmBgYHtyIEFnZUdyb3Vwc30KbGlicmFyeShkcGx5cikKCkFFUk5BU0UuY2xpbiA8LSBBRVJOQVNFLmNsaW4gJT4lIGRwbHlyOjptdXRhdGUoQWdlR3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKEFnZSA8IDU1IH4gIjw1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDU1ICAmIEFnZSA8PSA2NCB+ICI1NS02NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDY1ICAmIEFnZSA8PSA3NCB+ICI2NS03NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDc1ICAmIEFnZSA8PSA4NCB+ICI3NS04NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDg1IH4gIjg1KyIpKSkgCgpBRVJOQVNFLmNsaW4gPC0gQUVSTkFTRS5jbGluICU+JSBkcGx5cjo6bXV0YXRlKEFnZUdyb3VwU2V4ID0gZmFjdG9yKGNhc2Vfd2hlbihBZ2UgPCA1NSAmIEdlbmRlciA9PSAibWFsZSIgfiAiPDU1IG1hbGVzIiAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDU1ICAmIEFnZSA8PSA2NCAmIEdlbmRlciA9PSAibWFsZSJ+ICI1NS02NCBtYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDY1ICAmIEFnZSA8PSA3NCAmIEdlbmRlciA9PSAibWFsZSJ+ICI2NS03NCBtYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDc1ICAmIEFnZSA8PSA4NCAmIEdlbmRlciA9PSAibWFsZSJ+ICI3NS04NCBtYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDg1ICYgR2VuZGVyID09ICJtYWxlIn4gIjg1KyBtYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlIDwgNTUgJiBHZW5kZXIgPT0gImZlbWFsZSIgfiAiPDU1IGZlbWFsZXMiICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBZ2UgPj0gNTUgICYgQWdlIDw9IDY0ICYgR2VuZGVyID09ICJmZW1hbGUifiAiNTUtNjQgZmVtYWxlcyAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFnZSA+PSA2NSAgJiBBZ2UgPD0gNzQgJiBHZW5kZXIgPT0gImZlbWFsZSJ+ICI2NS03NCBmZW1hbGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBZ2UgPj0gNzUgICYgQWdlIDw9IDg0ICYgR2VuZGVyID09ICJmZW1hbGUifiAiNzUtODQgZmVtYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWdlID49IDg1ICYgR2VuZGVyID09ICJmZW1hbGUifiAiODUrIGZlbWFsZXMiKSkpCgp0YWJsZShBRVJOQVNFLmNsaW4kQWdlR3JvdXAsIEFFUk5BU0UuY2xpbiRHZW5kZXIpCnRhYmxlKEFFUk5BU0UuY2xpbiRBZ2VHcm91cFNleCkKCmBgYAoKTm93IHdlIGNhbiBkcmF3IHNvbWUgZ3JhcGhzIG9mIHBsYXF1ZSBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHBlciBzZXggYW5kIGFnZSBncm91cCBhcyBtZWRpYW4gwrEgaW50ZXJxdWFydGlsZSByYW5nZS4KCmBgYHtyIHBlciBBZ2VHcm91cCBwZXIgU2V4fQoKIyA/Z2dwdWJyOjpnZ2JveHBsb3QoKQpjb21wYXJlX21lYW5zKEhNT1gxIH4gQWdlR3JvdXAsICBkYXRhID0gQUVSTkFTRS5jbGluLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkFnZUdyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJBZ2UgZ3JvdXBzICh5ZWFycykiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiQWdlR3JvdXAiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgICMgYWRkID0gIm1lZGlhbl9pcXIiKQogICAgICAgICAgICAgICAgICBhZGQgPSBjKCJtZWRpYW5faXFyIiwgImppdHRlciIpKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEFnZUdyb3VwKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkFnZUdyb3VwLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gQWdlR3JvdXAsIGdyb3VwLmJ5ID0gIkdlbmRlciIsIGRhdGEgPSBBRVJOQVNFLmNsaW4sIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4sIAogICAgICAgICAgICAgICAgICB4ID0gYygiQWdlR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkFnZSBncm91cHMgKHllYXJzKSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICAjIGFkZCA9ICJtZWRpYW5faXFyIikKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuQWdlR3JvdXBfcGVyR2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgoKCgpgYGAKCgojIyBIeXBlcnRlbnNpb24gJiBibG9vZCBwcmVzc3VyZQpXZSB3YW50IHRvIGNyZWF0ZSBmaWd1cmVzIG9mIGByIFRSQUlUX09GX0lOVEVSRVNUYCBsZXZlbHMgc3RyYXRpZmllZCBieSBoeXBlcnRlbnNpb24vYmxvb2QgcHJlc3N1cmUsIGFuZCB1c2Ugb2YgYW50aS1oeXBlcnRlbnNpdmUgZHJ1Z3MuIAoKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgYHIgVFJBSVRfT0ZfSU5URVJFU1RgIHBsYXF1ZSBsZXZlbHMgYnkgaHlwZXJ0ZW5zaW9uIGdyb3VwIChubywgeWVzKQotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciBgciBUUkFJVF9PRl9JTlRFUkVTVGAgcGxhcXVlIGxldmVscyBieSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBncm91cCAoPDEyMCwgMTIwLTEzOSwgMTQwLTE1OSwxNjArKQoKCmBgYHtyIEJsb29kUHJlc3N1cmV9CmxpYnJhcnkoZHBseXIpCgpBRVJOQVNFLmNsaW4gPC0gQUVSTkFTRS5jbGluICU+JSBtdXRhdGUoU0JQR3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKHN5c3RvbGljIDwgMTIwIH4gIjwxMjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN5c3RvbGljID49IDEyMCAgJiBzeXN0b2xpYyA8PSAxMzkgfiAiMTIwLTEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3lzdG9saWMgPj0gMTQwICAmIHN5c3RvbGljIDw9IDE1OSB+ICIxNDAtMTU5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzeXN0b2xpYyA+PSAxNjAgfiAiMTYwKyIpKSkgCgp0YWJsZShBRVJOQVNFLmNsaW4kU0JQR3JvdXAsIEFFUk5BU0UuY2xpbiRHZW5kZXIpCgpgYGAKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgYHIgVFJBSVRfT0ZfSU5URVJFU1RgIGxldmVscyBwZXIgc2V4IGFuZCBoeXBlcnRlbnNpb24vYmxvb2QgcHJlc3N1cmUgZ3JvdXAgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgojIyMgSE1PWDEKCmBgYHtyfQpkZXRhY2goInBhY2thZ2U6RW5zRGIuSHNhcGllbnMudjg2IiwgdW5sb2FkID0gVFJVRSkKZGV0YWNoKCJwYWNrYWdlOmVuc2VtYmxkYiIsIHVubG9hZCA9IFRSVUUpCgpgYGAKCgpgYGB7ciB9CmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBTQlBHcm91cCwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShTQlBHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKFNCUEdyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiU0JQR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlN5c3RvbGljIGJsb29kIHByZXNzdXJlIChtbUhnKSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJTQlBHcm91cCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gU0JQR3JvdXApLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuU0JQR3JvdXAucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVydGVuc2lvbi5zZWxmcmVwb3J0KSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlNlbGYtcmVwb3J0ZWQgaHlwZXJ0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkh5cGVydGVuc2lvbi5zZWxmcmVwb3J0IiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5IeXBlcnRlbnNpb24ucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBIeXBlcnRlbnNpb24uZHJ1Z3MsIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoSHlwZXJ0ZW5zaW9uLmRydWdzKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoSHlwZXJ0ZW5zaW9uLmRydWdzKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiSHlwZXJ0ZW5zaW9uLmRydWdzIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJIeXBlcnRlbnNpb24gbWVkaWNhdGlvbiB1c2UiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiSHlwZXJ0ZW5zaW9uLmRydWdzIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBIeXBlcnRlbnNpb24uZHJ1Z3MpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuSHlwZXJ0ZW5zaW9uRHJ1Z3MucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCgoKY29tcGFyZV9tZWFucyhITU9YMSB+IFNCUEdyb3VwLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKFNCUEdyb3VwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoU0JQR3JvdXApKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJTQlBHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgKG1tSGcpIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuU0JQR3JvdXBfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVydGVuc2lvbi5zZWxmcmVwb3J0KSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlNlbGYtcmVwb3J0ZWQgaHlwZXJ0ZW5zaW9uIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuSHlwZXJ0ZW5zaW9uX2J5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gSHlwZXJ0ZW5zaW9uLmRydWdzLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVydGVuc2lvbi5kcnVncykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVydGVuc2lvbi5kcnVncykpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVydGVuc2lvbi5kcnVncyIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiSHlwZXJ0ZW5zaW9uIG1lZGljYXRpb24gdXNlIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuSHlwZXJ0ZW5zaW9uLmRydWdzX2J5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgoKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBTQlBHcm91cCwgZ3JvdXAuYnkgPSAiSHlwZXJ0ZW5zaW9uLmRydWdzIiwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShTQlBHcm91cCkgJiAhaXMubmEoSHlwZXJ0ZW5zaW9uLmRydWdzKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoU0JQR3JvdXApICYgIWlzLm5hKEh5cGVydGVuc2lvbi5kcnVncykpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIlNCUEdyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTeXN0b2xpYyBibG9vZCBwcmVzc3VyZSAobW1IZykgYnkgbWVkaWNhdGlvbiB1c2UiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiSHlwZXJ0ZW5zaW9uLmRydWdzIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiM0OUEwMUQiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBIeXBlcnRlbnNpb24uZHJ1Z3MpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuU0JQR3JvdXBfYnlIeXBlcnRlbnNpb25EcnVncy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhITU9YMSB+IEh5cGVydGVuc2lvbi5zZWxmcmVwb3J0LCBncm91cC5ieSA9ICJIeXBlcnRlbnNpb24uZHJ1Z3MiLCBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVydGVuc2lvbi5zZWxmcmVwb3J0KSAmICFpcy5uYShIeXBlcnRlbnNpb24uZHJ1Z3MpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcnRlbnNpb24uc2VsZnJlcG9ydCkgJiAhaXMubmEoSHlwZXJ0ZW5zaW9uLmRydWdzKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiSHlwZXJ0ZW5zaW9uLnNlbGZyZXBvcnQiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlNlbGYtcmVwb3J0ZWQgaHlwZXJ0ZW5zaW9uIHBlciBtZWRpY2F0aW9uIHVzZSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJIeXBlcnRlbnNpb24uZHJ1Z3MiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiIzQ5QTAxRCIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEh5cGVydGVuc2lvbi5kcnVncyksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5IeXBlcnRlbnNpb24uc2VsZnJlcG9ydF9ieUh5cGVydGVuc2lvbkRydWdzLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCgojIyBIeXBlcmNob2xlc3Rlcm9sZW1pYSAmIExETCBsZXZlbHMKV2Ugd2FudCB0byBjcmVhdGUgZmlndXJlcyBvZiBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHN0cmF0aWZpZWQgYnkgaHlwZXJjaG9sZXN0ZXJvbGVtaWEvTERMLWxldmVscywgYW5kIHVzZSBvZiBsaXBpZC1sb3dlcmluZyBkcnVncy4gCgotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciBgciBUUkFJVF9PRl9JTlRFUkVTVGAgcGxhcXVlIGxldmVscyBieSBoeXBlcmNob2xlc3Rlcm9sZW1pYSAoYHJpc2s2MTRgKSBncm91cCAobm8sIHllcykKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgYHIgVFJBSVRfT0ZfSU5URVJFU1RgIHBsYXF1ZSBsZXZlbHMgYnkgbGlwaWQtbG93ZXJpbmcgZHJ1Z3MgZ3JvdXAgKG5vLCB5ZXMpCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIGByIFRSQUlUX09GX0lOVEVSRVNUYCBwbGFxdWUgbGV2ZWxzIGJ5IExETC1sZXZlbHMgKG1tb2wvTCkgZ3JvdXAgKDwxMDAsIDEwMC0xMjksIDEzMC0xNTksIDE2MC0xODksIDE5MCspCgpgYGB7ciBMRExHcm91cHN9CmxpYnJhcnkoZHBseXIpCgpBRVJOQVNFLmNsaW4gPC0gQUVSTkFTRS5jbGluICU+JSBtdXRhdGUoTERMR3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKExETF9maW5hbENVIDwgMTAwIH4gIjwxMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExETF9maW5hbENVID49IDEwMCAgJiBMRExfZmluYWxDVSA8PSAxMjkgfiAiMTAwLTEyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTERMX2ZpbmFsQ1UgPj0gMTMwICAmIExETF9maW5hbENVIDw9IDE1OSB+ICIxMzAtMTU5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMRExfZmluYWxDVSA+PSAxNjAgICYgTERMX2ZpbmFsQ1UgPD0gMTg5IH4gIjE2MC0xODkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExETF9maW5hbENVID49IDE5MCB+ICIxOTArIikpKSAKCgp0YWJsZShBRVJOQVNFLmNsaW4kTERMR3JvdXAsIEFFUk5BU0UuY2xpbiRHZW5kZXIpCgpgYGAKCgpgYGB7ciBGaXggSHlwZXJjaG9sZXN0ZXJvbGVtaWEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJlcXVpcmUoc2psYWJlbGxlZCkKCkFFUk5BU0UuY2xpbiRyaXNrNjE0IDwtIHRvX2ZhY3RvcihBRVJOQVNFLmNsaW4kcmlzazYxNCkKCiMgRml4IHBsYXF1ZXBoZW5vdHlwZXMKYXR0YWNoKEFFUk5BU0UuY2xpbikKQUVSTkFTRS5jbGluWywiSHlwZXJjaG9sZXN0ZXJvbGVtaWEiXSA8LSBOQQpBRVJOQVNFLmNsaW4kSHlwZXJjaG9sZXN0ZXJvbGVtaWFbcmlzazYxNCA9PSAibWlzc2luZyB2YWx1ZSJdIDwtIE5BCkFFUk5BU0UuY2xpbiRIeXBlcmNob2xlc3Rlcm9sZW1pYVtyaXNrNjE0ID09IC05OTldIDwtIE5BCkFFUk5BU0UuY2xpbiRIeXBlcmNob2xlc3Rlcm9sZW1pYVtyaXNrNjE0ID09IDBdIDwtICJubyIKQUVSTkFTRS5jbGluJEh5cGVyY2hvbGVzdGVyb2xlbWlhW3Jpc2s2MTQgPT0gMV0gPC0gInllcyIKZGV0YWNoKEFFUk5BU0UuY2xpbikKCnRhYmxlKEFFUk5BU0UuY2xpbiRyaXNrNjE0LCBBRVJOQVNFLmNsaW4kSHlwZXJjaG9sZXN0ZXJvbGVtaWEpCgojIEFFREIudGVtcCA8LSBzdWJzZXQoQUVEQiwgIHNlbGVjdCA9IGMoIlNUVURZX05VTUJFUiIsICJVUElEIiwgIkFnZSIsICJHZW5kZXIiLCAiSG9zcGl0YWwiLCAiQXJ0ZXJ5X3N1bW1hcnkiLCAicmlzazYxNCIsICJIeXBlcmNob2xlc3Rlcm9sZW1pYSIpKQojIHJlcXVpcmUobGFiZWxsZWQpCiMgQUVEQi50ZW1wJEdlbmRlciA8LSB0b19mYWN0b3IoQUVEQi50ZW1wJEdlbmRlcikKIyBBRURCLnRlbXAkSG9zcGl0YWwgPC0gdG9fZmFjdG9yKEFFREIudGVtcCRIb3NwaXRhbCkKIyBBRURCLnRlbXAkQXJ0ZXJ5X3N1bW1hcnkgPC0gdG9fZmFjdG9yKEFFREIudGVtcCRBcnRlcnlfc3VtbWFyeSkKIyAKIyBEVDo6ZGF0YXRhYmxlKEFFREIudGVtcFsxOjEwLF0sIGNhcHRpb24gPSAiRXhjZXJwdCBvZiB0aGUgd2hvbGUgQUVEQi4iLCByb3duYW1lcyA9IEZBTFNFKQojIAojIHJtKEFFREIudGVtcCkKCmBgYAoKTm93IHdlIGNhbiBkcmF3IHNvbWUgZ3JhcGhzIG9mIHBsYXF1ZSBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHBlciBzZXggYW5kIGh5cGVyY2hvbGVzdGVyb2xlbWlhL0xETC1sZXZlbHMgZ3JvdXAsIGFzIHdlbGwgYXMgc3RyYXRpZmllZCBieSBsaXBpZC1sb3dlcmluZyBkcnVncyB1c2VycyBhcyBtZWRpYW4gwrEgaW50ZXJxdWFydGlsZSByYW5nZS4KCiMjIyBITU9YMQoKYGBge3IgfQoKY29tcGFyZV9tZWFucyhITU9YMSB+IExETEdyb3VwLCBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKExETEdyb3VwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoTERMR3JvdXApKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJMRExHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiTERMIChtZy9kTCkgcGVyIGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikpIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiTERMR3JvdXAiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5MRExHcm91cHMucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBMRExHcm91cCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShMRExHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKExETEdyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiTERMR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkxETCAobWcvZEwpIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkxETEdyb3Vwc19ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gSHlwZXJjaG9sZXN0ZXJvbGVtaWEsIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoSHlwZXJjaG9sZXN0ZXJvbGVtaWEpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcmNob2xlc3Rlcm9sZW1pYSkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVyY2hvbGVzdGVyb2xlbWlhIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJEaWFnbm9zZWQgaHlwZXJjaG9sZXN0ZXJvbGVtaWEiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkh5cGVyY2hvbGVzdGVyb2xlbWlhIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuSHlwZXJjaG9sZXN0ZXJvbGVtaWEucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBIeXBlcmNob2xlc3Rlcm9sZW1pYSwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShIeXBlcmNob2xlc3Rlcm9sZW1pYSkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVyY2hvbGVzdGVyb2xlbWlhKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiSHlwZXJjaG9sZXN0ZXJvbGVtaWEiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkRpYWdub3NlZCBoeXBlcmNob2xlc3Rlcm9sZW1pYSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSkiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5IeXBlcmNob2xlc3Rlcm9sZW1pYV9ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBNZWQuU3RhdGluLkxMRCwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShNZWQuU3RhdGluLkxMRCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKE1lZC5TdGF0aW4uTExEKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiTWVkLlN0YXRpbi5MTEQiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkxpcGlkLWxvd2VyaW5nIGRydWcgdXNlIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSkiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJNZWQuU3RhdGluLkxMRCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLk1lZC5TdGF0aW4uTExELnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gTWVkLlN0YXRpbi5MTEQsIGdyb3VwLmJ5ID0gIkdlbmRlciIsIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoTWVkLlN0YXRpbi5MTEQpKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShNZWQuU3RhdGluLkxMRCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIk1lZC5TdGF0aW4uTExEIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJMaXBpZC1sb3dlcmluZyBkcnVnIHVzZSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSkiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5NZWQuU3RhdGluLkxMRF9ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKCgoKY29tcGFyZV9tZWFucyhITU9YMSB+IExETEdyb3VwLCBncm91cC5ieSA9ICJNZWQuU3RhdGluLkxMRCIsIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoTERMR3JvdXApICYgIWlzLm5hKE1lZC5TdGF0aW4uTExEKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoTERMR3JvdXApICYgIWlzLm5hKE1lZC5TdGF0aW4uTExEKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiTERMR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkxETCAobWcvZEwpIHBlciBMTEQgdXNlIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSkiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJNZWQuU3RhdGluLkxMRCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjNDlBMDFEIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gTWVkLlN0YXRpbi5MTEQpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuTERMR3JvdXBzX2J5TWVkLlN0YXRpbi5MTEQucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBIeXBlcmNob2xlc3Rlcm9sZW1pYSwgZ3JvdXAuYnkgPSAiTWVkLlN0YXRpbi5MTEQiLCBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVyY2hvbGVzdGVyb2xlbWlhKSAmICFpcy5uYShNZWQuU3RhdGluLkxMRCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEh5cGVyY2hvbGVzdGVyb2xlbWlhKSAmICFpcy5uYShNZWQuU3RhdGluLkxMRCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkh5cGVyY2hvbGVzdGVyb2xlbWlhIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJEaWFnbm9zZWQgaHlwZXJjaG9sZXN0ZXJvbGVtaWEgcGVyIExMRCB1c2UiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIk1lZC5TdGF0aW4uTExEIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiM0OUEwMUQiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBNZWQuU3RhdGluLkxMRCksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5MRExHcm91cHNfYnlNZWQuU3RhdGluLkxMRC5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKCmBgYAoKCiMjIEtpZG5leSBmdW5jdGlvbiAoZUdGUikKV2Ugd2FudCB0byBjcmVhdGUgZmlndXJlcyBvZiBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHN0cmF0aWZpZWQgYnkga2lkbmV5IGZ1bmN0aW9uLiAKCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIGByIFRSQUlUX09GX0lOVEVSRVNUYCBwbGFxdWUgbGV2ZWxzIGJ5IGNocm9uaWMga2lkbmV5IGRpc2Vhc2UgKENLRCkgZ3JvdXAgKDEsIDIsIDMsIDQsIDUpCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIGByIFRSQUlUX09GX0lOVEVSRVNUYCBwbGFxdWUgbGV2ZWxzIGJ5IGVHRlIgKE1EUkQtYmFzZWQpIGdyb3VwICg5MCssIDYwLTg5LCAzMC01OSwgPDMwKQoKYGBge3IgRUdGUn0KbGlicmFyeShkcGx5cikKCkFFUk5BU0UuY2xpbiA8LSBBRVJOQVNFLmNsaW4gJT4lIG11dGF0ZShlR0ZSR3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKEdGUl9NRFJEIDwgMTUgfiAiPDE1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdGUl9NRFJEID49IDE1ICAmIEdGUl9NRFJEIDw9IDI5IH4gIjE1LTI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdGUl9NRFJEID49IDMwICAmIEdGUl9NRFJEIDw9IDU5IH4gIjMwLTU5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdGUl9NRFJEID49IDYwICAmIEdGUl9NRFJEIDw9IDg5IH4gIjYwLTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdGUl9NRFJEID49IDkwIH4gIjkwKyIpKSkKCnRhYmxlKEFFUk5BU0UuY2xpbiRlR0ZSR3JvdXAsIEFFUk5BU0UuY2xpbiRHZW5kZXIpCgp0YWJsZShBRVJOQVNFLmNsaW4kZUdGUkdyb3VwLCBBRVJOQVNFLmNsaW4kS0RPUUkpCgpgYGAKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgYHIgVFJBSVRfT0ZfSU5URVJFU1RgIGxldmVscyBwZXIgc2V4IGFuZCBraWRuZXkgZnVuY3Rpb24gZ3JvdXAgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgojIyMgSE1PWDEKCmBgYHtyfQoKIyBHbG9iYWwgdGVzdAoKY29tcGFyZV9tZWFucyhITU9YMSB+IGVHRlJHcm91cCwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShlR0ZSR3JvdXApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShlR0ZSR3JvdXApKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJlR0ZSR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gImVHRlIgKG1ML21pbiBwZXIgMS43MyBtMikiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZUdGUkdyb3VwIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkVHRlIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBlR0ZSR3JvdXAsIGdyb3VwLmJ5ID0gIkdlbmRlciIsICBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKGVHRlJHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKGVHRlJHcm91cCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoImVHRlJHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiZUdGUiAobUwvbWluIHBlciAxLjczIG0yKSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkVHRlJfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBLRE9RSSwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShLRE9RSSkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEtET1FJKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiS0RPUUkiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIktpZG5leSBmdW5jdGlvbiAoS0RPUUkpIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIktET1FJIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJucGciLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBLRE9RSSksIGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHAxICsgcm90YXRlX3hfdGV4dCg0NSksIGxlZ2VuZCA9ICJyaWdodCIpIApybShwMSkKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLktET1FJLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gS0RPUUksIGdyb3VwLmJ5ID0gIkdlbmRlciIsICAgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShLRE9RSSkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEtET1FJKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiS0RPUUkiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIktpZG5leSBmdW5jdGlvbiAoS0RPUUkpIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMSArIHJvdGF0ZV94X3RleHQoNDUpLCBsZWdlbmQgPSAicmlnaHQiKSAKcm0ocDEpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5LRE9RSV9ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhITU9YMSB+IGVHRlJHcm91cCwgIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoZUdGUkdyb3VwKSAmICFpcy5uYShLRE9RSSkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKGVHRlJHcm91cCkgJiAhaXMubmEoS0RPUUkpKSwgCiAgICAgICAgICAgICAgICAgIHggPSBjKCJlR0ZSR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gImVHRlIgKG1ML21pbiBwZXIgMS43MyBtMikgYnkgS0RPUUkgZ3JvdXAiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiS0RPUUkiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3BhcihwMSwgbGVnZW5kID0gInJpZ2h0IikKcm0ocDEpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5FR0ZSX0tET1FJLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCgojIyBCTUkKV2Ugd2FudCB0byBjcmVhdGUgZmlndXJlcyBvZiBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHN0cmF0aWZpZWQgYnkgQk1JLiAKCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIGByIFRSQUlUX09GX0lOVEVSRVNUYCBwbGFxdWUgbGV2ZWxzIGJ5IEJNSSBXSE8gZ3JvdXAgKHVuZGVyd2VpZ2h0LCBub3JtYWwsIG92ZXJ3ZWlnaHQsIG9iZXNlKQotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciBgciBUUkFJVF9PRl9JTlRFUkVTVGAgcGxhcXVlIGxldmVscyBieSBCTUkgZ3JvdXAgKDwxOC41LCAxOC41LTI0LjksIDI1LCAyOS45LCAzMC0yNC45LCAzNSspCgpgYGB7ciBCTUl9CmxpYnJhcnkoZHBseXIpCgpBRVJOQVNFLmNsaW4gPC0gQUVSTkFTRS5jbGluICU+JSBtdXRhdGUoQk1JR3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKEJNSSA8IDE4LjUgfiAiPDE4LjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJNSSA+PSAxOC41ICAmIEJNSSA8IDI1IH4gIjE4LjUtMjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJNSSA+PSAyNSAgJiBCTUkgPCAzMCB+ICIyNS0yOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQk1JID49IDMwICAmIEJNSSA8IDM1IH4gIjMwLTM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCTUkgPj0gMzUgfiAiMzUrIikpKSAKCiMgcmVxdWlyZShsYWJlbGxlZCkKIyBBRVJOQVNFLmNsaW4kQk1JX1VTIDwtIGFzX2ZhY3RvcihBRVJOQVNFLmNsaW4kQk1JX1VTKQojIEFFUk5BU0UuY2xpbiRCTUlfV0hPIDwtIGFzX2ZhY3RvcihBRVJOQVNFLmNsaW4kQk1JX1dITykKIyB0YWJsZShBRVJOQVNFLmNsaW4kQk1JX1dITywgQUVSTkFTRS5jbGluJEJNSV9VUykKCnRhYmxlKEFFUk5BU0UuY2xpbiRCTUlHcm91cCwgQUVSTkFTRS5jbGluJEdlbmRlcikKdGFibGUoQUVSTkFTRS5jbGluJEJNSUdyb3VwLCBBRVJOQVNFLmNsaW4kQk1JX1dITykKCmBgYAoKTm93IHdlIGNhbiBkcmF3IHNvbWUgZ3JhcGhzIG9mIHBsYXF1ZSBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHBlciBzZXggYW5kIGFnZSBncm91cCBhcyBtZWRpYW4gwrEgaW50ZXJxdWFydGlsZSByYW5nZS4KCiMjIyBITU9YMQoKYGBge3J9CgojIEdsb2JhbCB0ZXN0CmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBCTUlHcm91cCwgIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoQk1JR3JvdXApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShCTUlHcm91cCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIkJNSUdyb3VwIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJCTUkgZ3JvdXBzIChrZy9tMikiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgIyBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICAjIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiQk1JR3JvdXAiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9ICJqaXR0ZXIiKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGxhYmVsID0gInAuZm9ybWF0IiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5CTUkucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmNvbXBhcmVfbWVhbnMoSE1PWDEgfiBCTUlHcm91cCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShCTUlHcm91cCkpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEJNSUdyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiQk1JR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkJNSSBncm91cHMgKGtnL20yKSBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkJNSV9ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhITU9YMSB+IEJNSUdyb3VwLCAgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShCTUlHcm91cCkgJiAhaXMubmEoQk1JX1dITykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDEgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKEJNSUdyb3VwKSAmICFpcy5uYShCTUlfV0hPKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiQk1JR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIkJNSSBncm91cHMgKGtnL20yKSBwZXIgV0hPIGNhdGVnb3JpZXMiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiQk1JX1dITyIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHAxLCBsZWdlbmQgPSAicmlnaHQiKQpybShwMSkKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkJNSV9ieVdITy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgoKIyMgRGlhYmV0ZXMKV2Ugd2FudCB0byBjcmVhdGUgZmlndXJlcyBvZiBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHN0cmF0aWZpZWQgYnkgdHlwZSAyIGRpYWJldGVzLiAKCi0gQm94IGFuZCBXaGlza2VyIHBsb3QgZm9yIGByIFRSQUlUX09GX0lOVEVSRVNUYCBwbGFxdWUgbGV2ZWxzIGJ5IHR5cGUgMiBkaWFiZXRlcyBncm91cCAobm8sIHllcykKCk5vdyB3ZSBjYW4gZHJhdyBzb21lIGdyYXBocyBvZiBwbGFxdWUgYHIgVFJBSVRfT0ZfSU5URVJFU1RgIGxldmVscyBwZXIgc2V4IGFuZCBhZ2UgZ3JvdXAgYXMgbWVkaWFuIMKxIGludGVycXVhcnRpbGUgcmFuZ2UuCgojIyMgSE1PWDEKCmBgYHtyIHBlciBEaWFiZXRlcyBwZXIgU2V4fQoKY29tcGFyZV9tZWFucyhITU9YMSB+IERpYWJldGVzU3RhdHVzLCAgCiAgICAgICAgICAgICAgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShEaWFiZXRlc1N0YXR1cykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKERpYWJldGVzU3RhdHVzKSksCiAgICAgICAgICAgICAgICAgIHggPSBjKCJEaWFiZXRlc1N0YXR1cyIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJEaWFiZXRlcyBzdGF0dXMiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgIyBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICAjIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiRGlhYmV0ZXNTdGF0dXMiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGFkZCA9IGMoIm1lZGlhbl9pcXIiLCAiaml0dGVyIikpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkRpYWJldGVzLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgoKY29tcGFyZV9tZWFucyhITU9YMSB+IERpYWJldGVzU3RhdHVzLCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKERpYWJldGVzU3RhdHVzKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoRGlhYmV0ZXNTdGF0dXMpKSwKICAgICAgICAgICAgICAgICAgeCA9IGMoIkRpYWJldGVzU3RhdHVzIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLAogICAgICAgICAgICAgICAgICB4bGFiID0gIkRpYWJldGVzIHN0YXR1cyBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuRGlhYmV0ZXNfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKICAKCmBgYAoKCiMjIFNtb2tpbmcKV2Ugd2FudCB0byBjcmVhdGUgZmlndXJlcyBvZiBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHN0cmF0aWZpZWQgYnkgc21va2luZy4gCgotIEJveCBhbmQgV2hpc2tlciBwbG90IGZvciBgciBUUkFJVF9PRl9JTlRFUkVTVGAgcGxhcXVlIGxldmVscyBieSBzbW9raW5nIGdyb3VwIChuZXZlciwgZXgsIGN1cnJlbnQpCgpOb3cgd2UgY2FuIGRyYXcgc29tZSBncmFwaHMgb2YgcGxhcXVlIGByIFRSQUlUX09GX0lOVEVSRVNUYCBsZXZlbHMgcGVyIHNleCBhbmQgYWdlIGdyb3VwIGFzIG1lZGlhbiDCsSBpbnRlcnF1YXJ0aWxlIHJhbmdlLgoKIyMjIEhNT1gxIAoKYGBge3IgcGVyIFNtb2tpbmcgcGVyIFNleH0KCiMgR2xvYmFsIHRlc3QKY29tcGFyZV9tZWFucyhITU9YMSB+IFNtb2tlclN0YXR1cywgIGRhdGEgPSBBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoU21va2VyU3RhdHVzKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoU21va2VyU3RhdHVzKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiU21va2VyU3RhdHVzIiksCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTbW9rZXIgc3RhdHVzIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgICMgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgIyBwYWxldHRlID0gYygiI0Q1MjY3QiIsICIjMTI5MEQ5IiksCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIlNtb2tlclN0YXR1cyIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuU21va2luZy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhITU9YMSB+IFNtb2tlclN0YXR1cywgZ3JvdXAuYnkgPSJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKFNtb2tlclN0YXR1cykpLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKFNtb2tlclN0YXR1cykpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIlNtb2tlclN0YXR1cyIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU21va2VyIHN0YXR1cyBwZXIgZ2VuZGVyIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gYygibWVkaWFuX2lxciIsICJqaXR0ZXIiKSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuU21va2luZ19ieUdlbmRlci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKYGBgCgoKIyMgU3Rlbm9zaXMKV2Ugd2FudCB0byBjcmVhdGUgZmlndXJlcyBvZiBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHN0cmF0aWZpZWQgYnkgc3Rlbm9zaXMgZ3JhZGUuIAoKLSBCb3ggYW5kIFdoaXNrZXIgcGxvdCBmb3IgYHIgVFJBSVRfT0ZfSU5URVJFU1RgIHBsYXF1ZSBsZXZlbHMgYnkgc3Rlbm9zaXMgZ3JhZGUgZ3JvdXAgKDw3MCwgNzAtODksIDkwKykKCmBgYHtyIFN0ZW5vc2lzfQpsaWJyYXJ5KGRwbHlyKQoKQUVSTkFTRS5jbGluIDwtIEFFUk5BU0UuY2xpbiAlPiUgbXV0YXRlKFN0ZW5vdGljR3JvdXAgPSBmYWN0b3IoY2FzZV93aGVuKHN0ZW5vc2UgPT0gIjAtNDklIiB+ICI8NzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZW5vc2UgPT0gIjAtNDklIiB+ICI8NzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZW5vc2UgPT0gIjUwLTcwJSIgfiAiPDcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVub3NlID09ICI3MC05MCUiIH4gIjcwLTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVub3NlID09ICI1MC05OSUiIH4gIjkwKyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Rlbm9zZSA9PSAiNzAtOTklIiB+ICI5MCsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0ZW5vc2UgPT0gIjEwMCUgKE9jY2x1c2lvbikiIH4gIjkwKyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Rlbm9zZSA9PSAiOTAtOTklIiB+ICI5MCsiKSkpCgp0YWJsZShBRVJOQVNFLmNsaW4kU3Rlbm90aWNHcm91cCwgQUVSTkFTRS5jbGluJEdlbmRlcikKdGFibGUoQUVSTkFTRS5jbGluJHN0ZW5vc2UsIEFFUk5BU0UuY2xpbiRTdGVub3RpY0dyb3VwKQoKYGBgCgpOb3cgd2UgY2FuIGRyYXcgc29tZSBncmFwaHMgb2YgcGxhcXVlIGByIFRSQUlUX09GX0lOVEVSRVNUYCBsZXZlbHMgcGVyIHNleCBhbmQgYWdlIGdyb3VwIGFzIG1lZGlhbiDCsSBpbnRlcnF1YXJ0aWxlIHJhbmdlLgoKIyMjIEhNT1gxCgpgYGB7ciBwZXIgU3Rlbm9zaXMgcGVyIFNleH0KCiMgR2xvYmFsIHRlc3QKY29tcGFyZV9tZWFucyhITU9YMSB+IFN0ZW5vdGljR3JvdXAsICBkYXRhID0gQUVSTkFTRS5jbGluICU+JSBmaWx0ZXIoIWlzLm5hKFN0ZW5vdGljR3JvdXApKSwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShTdGVub3RpY0dyb3VwKSksIAogICAgICAgICAgICAgICAgICB4ID0gYygiU3Rlbm90aWNHcm91cCIpLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiU3Rlbm90aWMgZ3JhZGUiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgIyBjb2xvciA9ICJHZW5kZXIiLAogICAgICAgICAgICAgICAgICAjIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3Rlbm90aWNHcm91cCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLlN0ZW5vc2lzLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gU3Rlbm90aWNHcm91cCwgZ3JvdXAuYnkgPSAiR2VuZGVyIiwgZGF0YSA9IEFFUk5BU0UuY2xpbiAlPiUgZmlsdGVyKCFpcy5uYShTdGVub3RpY0dyb3VwKSksIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4gJT4lIGZpbHRlcighaXMubmEoU3Rlbm90aWNHcm91cCkpLCAKICAgICAgICAgICAgICAgICAgeCA9IGMoIlN0ZW5vdGljR3JvdXAiKSwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlN0ZW5vdGljIGdyYWRlIHBlciBnZW5kZXIiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9IGMoIiNENTI2N0IiLCAiIzEyOTBEOSIpLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpnZ3NhdmUoZmlsZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuU3Rlbm9zaXNfYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCmBgYAoKCiMjIFN5bXB0b21zCldlIHdhbnQgdG8gY3JlYXRlIHBlci1zeW1wdG9tIGZpZ3VyZXMuIAoKYGBge3IgU3ltcHRvbUdyb3Vwc30KbGlicmFyeShkcGx5cikKCnRhYmxlKEFFUk5BU0UuY2xpbiRBZ2VHcm91cCwgQUVSTkFTRS5jbGluJEFzeW1wdFN5bXB0MkcpCnRhYmxlKEFFUk5BU0UuY2xpbiRHZW5kZXIsIEFFUk5BU0UuY2xpbiRBc3ltcHRTeW1wdDJHKQp0YWJsZShBRVJOQVNFLmNsaW4kQXN5bXB0U3ltcHQyRykKCmBgYAoKTm93IHdlIGNhbiBkcmF3IHNvbWUgZ3JhcGhzIG9mIHBsYXF1ZSBgciBUUkFJVF9PRl9JTlRFUkVTVGAgbGV2ZWxzIHBlciBzeW1wdG9tIGdyb3VwIGFzIG1lZGlhbiDCsSBpbnRlcnF1YXJ0aWxlIHJhbmdlLgoKIyMjIEhNT1gxCgpgYGB7ciBwZXIgU3ltcHRvbUdyb3Vwc30KCiMgP2dncHVicjo6Z2dib3hwbG90KCkKbXlfY29tcGFyaXNvbnMgPC0gbGlzdChjKCJBc3ltcHRvbWF0aWMiLCAiU3ltcHRvbWF0aWMiKSkKCnAxIDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiwgCiAgICAgICAgICAgICAgICAgIHggPSAiQXN5bXB0U3ltcHQyRyIsIHkgPSAiSE1PWDEiLAogICAgICAgICAgICAgICAgICB0aXRsZSA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSBsZXZlbHMgcGVyIHN5bXB0b20iLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJTeW1wdG9tcyIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJBc3ltcHRTeW1wdDJHIiwgCiAgICAgICAgICAgICAgICAgICMgcGFsZXR0ZSA9IGModWl0aG9mX2NvbG9yWzE2XSwgdWl0aG9mX2NvbG9yWzIzXSksCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImRvdHBsb3QiLCAjIEFkZCBkb3RwbG90CiAgICAgICAgICAgICAgICAgIGFkZC5wYXJhbXMgPSBsaXN0KGJpbndpZHRoID0gMC4xLCBkb3RzaXplID0gMC4zKQogICAgICAgICAgKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIpCmdncGFyKHAxLCBsZWdlbmQgPSBjKCJyaWdodCIpLCBsZWdlbmQudGl0bGUgPSAiU3ltcHRvbXMiKQoKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkFzeW1wdFN5bXB0MkcucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCnJtKHAxKQoKY29tcGFyZV9tZWFucyhITU9YMSB+IEFzeW1wdFN5bXB0MkcsIGdyb3VwLmJ5ID0gIkdlbmRlciIsIGRhdGEgPSBBRVJOQVNFLmNsaW4sIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwMSA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4sIAogICAgICAgICAgICAgICAgICB4ID0gIkFzeW1wdFN5bXB0MkciLCB5ID0gIkhNT1gxIiwKICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbikgbGV2ZWxzIHBlciBzeW1wdG9tIGJ5IGdlbmRlciIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlN5bXB0b21zIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKSIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImRvdHBsb3QiLCAjIEFkZCBkb3RwbG90CiAgICAgICAgICAgICAgICAgIGFkZC5wYXJhbXMgPSBsaXN0KGJpbndpZHRoID0gMC4xLCBkb3RzaXplID0gMC4zKQogICAgICAgICAgKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IEdlbmRlciksIGxhYmVsID0gInAuZm9ybWF0IiwgIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIpCmdncGFyKHAxLCBsZWdlbmQgPSBjKCJyaWdodCIpLCBsZWdlbmQudGl0bGUgPSAiU3ltcHRvbXMiKQoKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDEucGxhcXVlLkFzeW1wdFN5bXB0MkcuYnlHZW5kZXIucGRmIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKCnJtKHAxKQoKYGBgCgoKIyMgRm9yZXN0IHBsb3RzCgpXZSB3b3VsZCBhbHNvIGxpa2UgdG8gdmlzdWFsaXplIHRoZSBtdWx0aXZhcmlhYmxlIGFuYWx5c2VzIHJlc3VsdHMuCmBgYHtyIGxvYWQgbW9kZWwgZGF0YX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KG9wZW54bHN4KQptb2RlbDFfdGFyZ2V0IDwtIHJlYWQueGxzeChwYXN0ZTAoT1VUX2xvYywgIi8iLCBUb2RheSwgIi5BRVJOQVNFLmNsaW4udGFyZ2V0cy5CaW4uVW5pLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5SQU5LLlN5bXB0b21zLk1PREVMMS54bHN4IikpCm1vZGVsMl90YXJnZXQgPC0gcmVhZC54bHN4KHBhc3RlMChPVVRfbG9jLCAiLyIsIFRvZGF5LCAiLkFFUk5BU0UuY2xpbi50YXJnZXRzLkJpbi5NdWx0aS4iLFRSQUlUX09GX0lOVEVSRVNULCIuUkFOSy5TeW1wdG9tcy5NT0RFTDIueGxzeCIpKQptb2RlbDFfdGFyZ2V0JG1vZGVsIDwtICJ1bml2YXJpYXRlIgptb2RlbDJfdGFyZ2V0JG1vZGVsIDwtICJtdWx0aXZhcmlhdGUiCgptb2RlbHNfdGFyZ2V0IDwtIHJiaW5kKG1vZGVsMV90YXJnZXQsIG1vZGVsMl90YXJnZXQpCm1vZGVsc190YXJnZXQKCmBgYAoKRm9yZXN0IHBsb3RzLgoKIyMjIEhNT1gxCgpgYGB7ciBmb3Jlc3RwbG90c30KZGF0IDwtIGRhdGEuZnJhbWUoZ3JvdXAgPSBmYWN0b3IoYygiQWdlLCBzZXgtYWRqdXN0ZWQiLCAiQWdlLCBzZXgsIGFuZCBhZGp1c3RlZCBmb3IgcmlzayBmYWN0b3JzIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoIkFnZSwgc2V4LCBhbmQgYWRqdXN0ZWQgZm9yIHJpc2sgZmFjdG9ycyIsICJBZ2UsIHNleC1hZGp1c3RlZCIpKSwKICAgICAgICAgICAgICAgICAgY2VuID0gYyhtb2RlbHNfdGFyZ2V0JE9SW21vZGVsc190YXJnZXQkUHJlZGljdG9yPT0iSE1PWDEiXSksCiAgICAgICAgICAgICAgICAgIGxvdyA9IGMobW9kZWxzX3RhcmdldCRsb3c5NUNJW21vZGVsc190YXJnZXQkUHJlZGljdG9yPT0iSE1PWDEiXSksCiAgICAgICAgICAgICAgICAgIGhpZ2ggPSBjKG1vZGVsc190YXJnZXQkdXA5NUNJW21vZGVsc190YXJnZXQkUHJlZGljdG9yPT0iSE1PWDEiXSkpCgpmcCA8LSBnZ3Bsb3QoZGF0YSA9IGRhdCwgYWVzKHggPSBncm91cCwgeSA9IGNlbiwgeW1pbiA9IGxvdywgeW1heCA9IGhpZ2gpKSArCiAgZ2VvbV9wb2ludHJhbmdlKGxpbmV0eXBlID0gMiwgc2l6ZSA9IDEsIGNvbG91ciA9IGMoIiMxMjkwRDkiLCAiIzQ5QTAxRCIpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGx0eSA9IDIpICsgICMgYWRkIGEgZG90dGVkIGxpbmUgYXQgeD0xIGFmdGVyIGZsaXAKICBjb29yZF9mbGlwKHlsaW0gPSBjKDAuOCwgMS43KSkgKyAgIyBmbGlwIGNvb3JkaW5hdGVzIChwdXRzIGxhYmVscyBvbiB5IGF4aXMpCiAgeGxhYigiTW9kZWwiKSArIHlsYWIoIk9SICg5NSUgQ0kpIGZvciBzeW1wdG9tYXRpYyBwbGFxdWVzIikgKwogIGdndGl0bGUoIlBsYXF1ZSBITU9YMSBub3JtYWxpemVkIGV4cHJlc3Npb24gKDEgU0QgaW5jcmVtZW50LCBuID0gNjIyKSIpICsKICB0aGVtZV9taW5pbWFsKCkgICMgdXNlIGEgd2hpdGUgYmFja2dyb3VuZApwcmludChmcCkKCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5mb3Jlc3QucGRmIiksIHBsb3QgPSBmcCkKCnJtKGZwKQpgYGAKCgojIyBUYXJnZXQgZXhwcmVzc2lvbiB2cy4gY3l0b2tpbmVzIHBsYXF1ZSBsZXZlbHMgY29ycmVsYXRpb25zCgpXZSB3aWxsIHBsb3QgdGhlIGNvcnJlbGF0aW9ucyBvZiBvdGhlciBjeXRva2luZSBwbGFxdWUgbGV2ZWxzIHRvIHRoZSBgciBUUkFJVF9PRl9JTlRFUkVTVGAgcGxhcXVlIGxldmVscy4gVGhlc2UgaW5jbHVkZToKCi0gSUwyCi0gSUw0Ci0gSUw1Ci0gSUw2Ci0gSUw4Ci0gSUw5Ci0gSUwxMAotIElMMTIKLSBJTDEzCi0gSUwyMQotIElORkcKLSBUTkZBCi0gTUlGCi0gTUNQMQotIE1JUDFhCi0gUkFOVEVTCi0gTUlHCi0gSVAxMAotIEVvdGF4aW4xCi0gVEFSQwotIFBBUkMKLSBNREMKLSBPUEcKLSBzSUNBTTEKLSBWRUdGQQotIFRHRkIKCkluIGFkZGl0aW9uIHdlIHdpbGwgbG9vayBhdCB0aHJlZSBtZXRhbGxvcHJvdGVpbmFzZXMgd2hpY2ggd2VyZSBtZWFzdXJlZCB1c2luZyBhbiBhY3Rpdml0eSBhc3NheS4gCgotIE1NUDIKLSBNTVA4Ci0gTU1QOQoKVGhlIHByb3RlaW5zIHdlcmUgbWVhc3VyZWQgdXNpbmcgRkFDUyBhbmQgTFVNSU5FWC4gR2l2ZW4gdGhlIGRpZmZlcmVudCBwbGF0Zm9ybXMgdXNlZCAoRkFDUyB2cy4gTFVNSU5FWCksIHdlIHdpbGwgaW52ZXJzZSByYW5rLW5vcm1hbGl6ZSB0aGVzZSB2YXJpYWJsZXMgYXMgd2VsbCB0byBzY2FsZSB0aGVtIHRvIHRoZSBzYW1lIHNjYWxlIGFzIHRoZSBgciBUUkFJVF9PRl9JTlRFUkVTVGBgIHBsYXF1ZSBsZXZlbHMuCgoKV2Ugd2lsbCBzZXQgdGhlIG1lYXN1cmVtZW50cyB0aGF0IHlpZWxkZWQgJzAnIHRvIE5BLCBhcyBpdCBpcyB1bmxpa2VseSB0aGF0IGFueSBwcm90ZWluIGV2ZXIgaGFzIGV4YWN0bHkgMCBjb3BpZXMuIFRoZSAnMCcgeWllbGRlZCBkdXJpbmcgdGhlIGV4cGVyaW1lbnQgYXJlIGR1ZSB0byB0aGUgbGltaXRzIG9mIHRoZSBkZXRlY3Rpb24uCgojIyMgUHJlcGFyZSBkYXRhCgpgYGB7cn0KIyBmaXggbmFtZXMKbmFtZXMoQUVEQi5DRUEpW25hbWVzKEFFREIuQ0VBKSA9PSAiVkVGR0EiXSA8LSAiVkVHRkEiCgojIGZpeCBuYW1lcwpuYW1lcyhBRVJOQVNFLmNsaW4pW25hbWVzKEFFUk5BU0UuY2xpbikgPT0gIklMNiJdIDwtICJJTDZybmEiCm5hbWVzKEFFUk5BU0UuY2xpbilbbmFtZXMoQUVSTkFTRS5jbGluKSA9PSAiTU1QOSJdIDwtICJNTVA5cm5hIgoKY3l0b2tpbmVzIDwtIGMoIklMMiIsICJJTDQiLCAiSUw1IiwgIklMNiIsICJJTDgiLCAiSUw5IiwgIklMMTAiLCAiSUwxMiIsICJJTDEzIiwgIklMMjEiLCAKICAgICAgICAgICAgICAgIklORkciLCAiVE5GQSIsICJNSUYiLCAiTUNQMSIsICJNSVAxYSIsICJSQU5URVMiLCAiTUlHIiwgIklQMTAiLCAiRW90YXhpbjEiLCAKICAgICAgICAgICAgICAgIlRBUkMiLCAiUEFSQyIsICJNREMiLCAiT1BHIiwgInNJQ0FNMSIsICJWRUdGQSIsICJUR0ZCIikKbWV0YWxsb3Byb3RlaW5hc2VzIDwtIGMoIk1NUDIiLCAiTU1QOCIsICJNTVA5IikKCgpBRVJOQVNFLmNsaW4gPC0gbWVyZ2UoQUVSTkFTRS5jbGluLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldChBRURCLkNFQSwgc2VsZWN0ID0gYygiU1RVRFlfTlVNQkVSIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3l0b2tpbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFsbG9wcm90ZWluYXNlcykpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5LnggPSAic3R1ZHlfbnVtYmVyIiwgYnkueSA9ICJTVFVEWV9OVU1CRVIiLCBzb3J0ID0gVFJVRSwgYWxsLnggPSBUUlVFKQoKYGBgCgoKYGBge3IgVGFyZ2V0IHZzIEN5dG9raW5lcyBJTlJULCBwYWdlZC5wcmludD1UUlVFfQoKcHJvdGVpbnNfb2ZfaW50ZXJlc3QgPC0gYyhjeXRva2luZXMsIG1ldGFsbG9wcm90ZWluYXNlcykKCnByb3RlaW5zX29mX2ludGVyZXN0X3JhbmsgPSB1bmxpc3QobGFwcGx5KHByb3RlaW5zX29mX2ludGVyZXN0LCBwYXN0ZTAsICJfcmFuayIpKQoKIyBtYWtlIHZhcmlhYmxlcyBudW1lcmljcygpCkFFUk5BU0UuY2xpbiA8LSBBRVJOQVNFLmNsaW4gJT4lCiAgbXV0YXRlX2VhY2goZnVucyhhcy5udW1lcmljKSwgcHJvdGVpbnNfb2ZfaW50ZXJlc3QpCiAgCmZvcihQUk9URUlOIGluIDE6bGVuZ3RoKHByb3RlaW5zX29mX2ludGVyZXN0KSl7CgogIHZhci50ZW1wLnJhbmsgPSBwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rW1BST1RFSU5dCiAgdmFyLnRlbXAgPSBwcm90ZWluc19vZl9pbnRlcmVzdFtQUk9URUlOXQogIAogIGNhdChwYXN0ZTAoIlxuU2VsZWN0aW5nICIsIHZhci50ZW1wLCAiIGFuZCBzdGFuZGFyZGlzaW5nOiAiLCB2YXIudGVtcC5yYW5rLCIuXG4iKSkKICBjYXQocGFzdGUwKCIqIGNoYW5naW5nICIsIHZhci50ZW1wLCAiIHRvIG51bWVyaWMuXG4iKSkKCiAgIyBBRVJOQVNFLmNsaW4gPC0gIEFFUk5BU0UuY2xpbiAlPiUgbXV0YXRlKEFFUk5BU0UuY2xpblssdmFyLnRlbXBdID09IHJlcGxhY2UoQUVSTkFTRS5jbGluWyx2YXIudGVtcF0sIEFFUk5BU0UuY2xpblssdmFyLnRlbXBdPT0wLCBOQSkpCgogIEFFUk5BU0UuY2xpblssdmFyLnRlbXBdW0FFUk5BU0UuY2xpblssdmFyLnRlbXBdPT0wLjAwMDAwMF09TkEKCiAgY2F0KHBhc3RlMCgiKiBzdGFuZGFyZGlzaW5nICIsIHZhci50ZW1wLCAKICAgICAgICAgICAgICIgKG1lYW46ICIscm91bmQobWVhbighaXMubmEoQUVSTkFTRS5jbGluWyx2YXIudGVtcF0pKSwgZGlnaXRzID0gNiksCiAgICAgICAgICAgICAiLCBuID0gIixzdW0oIWlzLm5hKEFFUk5BU0UuY2xpblssdmFyLnRlbXBdKSksIikuXG4iKSkKICAKICBBRVJOQVNFLmNsaW4gPC0gQUVSTkFTRS5jbGluICU+JQogICAgICBtdXRhdGVfYXQodmFycyh2YXIudGVtcCksIAogICAgICAgICMgbGlzdChaID0gfiAoQUVSTkFTRS5jbGluWyx2YXIudGVtcF0gLSBtZWFuKEFFUk5BU0UuY2xpblssdmFyLnRlbXBdLCBuYS5ybSA9IFRSVUUpKS9zZChBRVJOQVNFLmNsaW5bLHZhci50ZW1wXSwgbmEucm0gPSBUUlVFKSkKICAgICAgICBsaXN0KFJBTksgPSB+IHFub3JtKChyYW5rKEFFUk5BU0UuY2xpblssdmFyLnRlbXBdLCBuYS5sYXN0ID0gImtlZXAiKSAtIDAuNSkgLyBzdW0oIWlzLm5hKEFFUk5BU0UuY2xpblssdmFyLnRlbXBdKSkpKQogICAgICApCiAgIyBzdHIoVUNPUkJJT0dTQXFjJFopCiAgY2F0KHBhc3RlMCgiKiByZW5hbWluZyBSQU5LIHRvICIsIHZhci50ZW1wLnJhbmssIi5cbiIpKQogIEFFUk5BU0UuY2xpblssdmFyLnRlbXAucmFua10gPC0gTlVMTAogIG5hbWVzKEFFUk5BU0UuY2xpbilbbmFtZXMoQUVSTkFTRS5jbGluKSA9PSAiUkFOSyJdIDwtIHZhci50ZW1wLnJhbmsKfQoKIyBybSh2YXIudGVtcCwgdmFyLnRlbXAucmFuaykKCmBgYAoKIyMjIFZpc3VhbGl6ZSB0cmFuc2Zvcm1hdGlvbnMKCldlIHdpbGwganVzdCB2aXN1YWxpemUgdGhlc2UgdHJhbnNmb3JtYXRpb25zLgoKYGBge3IgVGFyZ2V0IHZzIEN5dG9raW5lcyBIaXN0b2dyYW1zfQpwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rX3RhcmdldCA8LSBjKCJITU9YMSIsIHByb3RlaW5zX29mX2ludGVyZXN0X3JhbmspCgpwcm90ZWluc19vZl9pbnRlcmVzdF90YXJnZXQgPC0gYygiSE1PWDEiLCBwcm90ZWluc19vZl9pbnRlcmVzdCkKCmZvcihQUk9URUlOX0dFTkUgaW4gcHJvdGVpbnNfb2ZfaW50ZXJlc3RfdGFyZ2V0KXsKICBjYXQocGFzdGUwKCJQbG90dGluZyBwcm90ZWluICIsIFBST1RFSU5fR0VORSwgIi5cbiIpKQogIAogIHAxIDwtIGdncHVicjo6Z2doaXN0b2dyYW0oQUVSTkFTRS5jbGluLCBQUk9URUlOX0dFTkUsCiAgICAgICAgICAgICAgICAgICAgIyB5ID0gIi4uY291bnQuLiIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiIzEyOTBEOSIsICIjREIwMDNGIiksCiAgICAgICAgICAgICAgICAgICAgYWRkID0gIm1lYW4iLAogICAgICAgICAgICAgICAgICAgICMgcnVnID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAjIGFkZC5wYXJhbXMgPSAgbGlzdChjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gMiksCiAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBwYXN0ZTAoUFJPVEVJTl9HRU5FLCAiIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiksCiAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICIiLAogICAgICAgICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkpCiAgcHJpbnQocDEpCiAgCn0KCgpmb3IoUFJPVEVJTl9HRU5FIGluIHByb3RlaW5zX29mX2ludGVyZXN0X3JhbmtfdGFyZ2V0KXsKICBjYXQocGFzdGUwKCJQbG90dGluZyBwcm90ZWluICIsIFBST1RFSU5fR0VORSwgIi5cbiIpKQogIAogIHAxIDwtIGdncHVicjo6Z2doaXN0b2dyYW0oQUVSTkFTRS5jbGluLCBQUk9URUlOX0dFTkUsCiAgICAgICAgICAgICAgICAgICAgIyB5ID0gIi4uY291bnQuLiIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgICBwYWxldHRlID0gYygiIzEyOTBEOSIsICIjREIwMDNGIiksCiAgICAgICAgICAgICAgICAgICAgYWRkID0gIm1lYW4iLAogICAgICAgICAgICAgICAgICAgICMgcnVnID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAjIGFkZC5wYXJhbXMgPSAgbGlzdChjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gMiksCiAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBwYXN0ZTAoUFJPVEVJTl9HRU5FLCAiIChub3JtYWxpemVkIGV4cHJlc3Npb24pIiksCiAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJpbnZlcnNlLW5vcm1hbCB0cmFuc2Zvcm1hdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSkKICBwcmludChwMSkKICAKfQogIApgYGAKCiMjIyBDb3JyZWxhdGlvbnMKCkhlcmUgd2UgY2FsY3VsYXRlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGByIFRSQUlUX09GX0lOVEVSRVNUYCBhbmQgMjggb3RoZXIgY3l0b2tpbmVzLiBXZSB1c2UgU3BlYXJtYW4ncyB0ZXN0LCB0aHVzLCBjb3JyZWxhdGlvbnMgYSBnaXZlbiBpbiBfcmhvXy4gUGxlYXNlIG5vdGUgdGhlIGluZGljYXRpb25zIG9mIG1lYXN1cmVtZW50IG1ldGhvZHM6CgotIF9MXzogTFVNSU5FWAotIF9FXzogRUxJU0EKLSBfYV86IGFjdGl2aXR5IGFzc2F5CgpgYGB7ciBUYXJnZXQgdnMgQ3l0b2tpbmVzIGNvcnJlbGF0aW9uc30KIyBJbnN0YWxsYXRpb24gb2YgZ2djb3JycGxvdCgpCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KaWYoIXJlcXVpcmUoZGV2dG9vbHMpKSAKICBpbnN0YWxsLnBhY2thZ2VzLmF1dG8oImRldnRvb2xzIikKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJrYXNzYW1iYXJhL2dnY29ycnBsb3QiKQoKbGlicmFyeShnZ2NvcnJwbG90KQoKIyBDcmVhdGluZyBtYXRyaXggLSBpbnZlcnNlLXJhbmsgdHJhbnNmb3JtYXRpb24KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp0ZW1wIDwtIHN1YnNldChBRVJOQVNFLmNsaW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCA9IGMocHJvdGVpbnNfb2ZfaW50ZXJlc3RfcmFua190YXJnZXQpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCiMgc3RyKEFFREIuQ0VBLnRlbXApCm1hdHJpeC5SQU5LIDwtIGFzLm1hdHJpeCh0ZW1wKQpybSh0ZW1wKQoKY29ycl9iaW9tYXJrZXJzLnJhbmsgPC0gcm91bmQoY29yKG1hdHJpeC5SQU5LLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIiwgI3RoZSBjb3JyZWxhdGlvbiBvciBjb3ZhcmlhbmNlIGJldHdlZW4gZWFjaCBwYWlyIG9mIHZhcmlhYmxlcyBpcyBjb21wdXRlZCB1c2luZyBhbGwgY29tcGxldGUgcGFpcnMgb2Ygb2JzZXJ2YXRpb25zIG9uIHRob3NlIHZhcmlhYmxlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzcGVhcm1hbiIpLCAzKQojIGNvcnJfYmlvbWFya2Vycy5yYW5rCgpyZW5hbWVfcHJvdGVpbnNfb2ZfaW50ZXJlc3RfdGFyZ2V0IDwtIGMoIkhNT1gxIChSTkEpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJTDIiLCAiSUw0IiwgIklMNSIsICJJTDYiLCAiSUw4IiwgIklMOSIsICJJTDEwIiwgIklMMTIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIklMMTMgKEwpIiwgIklMMjEgKEwpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJTkZHIiwgIlRORkEiLCAiTUlGIChMKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUNQMSAoTCkiLCAiTUlQMWEgKEwpIiwgIlJBTlRFUyAoTCkiLCAiTUlHIChMKSIsICJJUDEwIChMKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRW90YXhpbjEgKEwpIiwgIlRBUkMgKEwpIiwgIlBBUkMgKEwpIiwgIk1EQyAoTCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9QRyAoTCkiLCAic0lDQU0xIChMKSIsICJWRUdGQSAoRSkiLCAiVEdGQiAoRSkiLCAiTU1QMiAoYSkiLCAiTU1QOCAoYSkiLCAiTU1QOSAoYSkiKQpjb2xuYW1lcyhjb3JyX2Jpb21hcmtlcnMucmFuaykgPC0gYyhyZW5hbWVfcHJvdGVpbnNfb2ZfaW50ZXJlc3RfdGFyZ2V0KQpyb3duYW1lcyhjb3JyX2Jpb21hcmtlcnMucmFuaykgPC0gYyhyZW5hbWVfcHJvdGVpbnNfb2ZfaW50ZXJlc3RfdGFyZ2V0KQoKY29ycl9iaW9tYXJrZXJzX3AucmFuayA8LSBnZ2NvcnJwbG90Ojpjb3JfcG1hdChtYXRyaXguUkFOSywgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCgojICsrKysrKysrKysrKysrKysrKysrKysrKysrKysKIyBmbGF0dGVuQ29yck1hdHJpeAojICsrKysrKysrKysrKysrKysrKysrKysrKysrKysKIyBjb3JtYXQgOiBtYXRyaXggb2YgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cwojIHBtYXQgOiBtYXRyaXggb2YgdGhlIGNvcnJlbGF0aW9uIHAtdmFsdWVzCmZsYXR0ZW5Db3JyTWF0cml4IDwtIGZ1bmN0aW9uKGNvcm1hdCwgcG1hdCkgewogIHV0IDwtIHVwcGVyLnRyaShjb3JtYXQpCiAgZGF0YS5mcmFtZSgKICAgIHJvdyA9IHJvd25hbWVzKGNvcm1hdClbcm93KGNvcm1hdClbdXRdXSwKICAgIGNvbHVtbiA9IHJvd25hbWVzKGNvcm1hdClbY29sKGNvcm1hdClbdXRdXSwKICAgIGNvciAgPShjb3JtYXQpW3V0XSwKICAgIHAgPSBwbWF0W3V0XQogICAgKQp9Cgpjb3JyX2Jpb21hcmtlcnMucmFuay5kZiA8LSBmbGF0dGVuQ29yck1hdHJpeChjb3JyX2Jpb21hcmtlcnMucmFuaywgY29ycl9iaW9tYXJrZXJzX3AucmFuaykKCgpuYW1lcyhjb3JyX2Jpb21hcmtlcnMucmFuay5kZilbbmFtZXMoY29ycl9iaW9tYXJrZXJzLnJhbmsuZGYpID09ICJyb3ciXSA8LSAiQ3l0b2tpbmVfWCIKbmFtZXMoY29ycl9iaW9tYXJrZXJzLnJhbmsuZGYpW25hbWVzKGNvcnJfYmlvbWFya2Vycy5yYW5rLmRmKSA9PSAiY29sdW1uIl0gPC0gIkN5dG9raW5lWSIKbmFtZXMoY29ycl9iaW9tYXJrZXJzLnJhbmsuZGYpW25hbWVzKGNvcnJfYmlvbWFya2Vycy5yYW5rLmRmKSA9PSAiY29yIl0gPC0gIlNwZWFybWFuUmhvIgoKRFQ6OmRhdGF0YWJsZShjb3JyX2Jpb21hcmtlcnMucmFuay5kZikKCmZ3cml0ZShjb3JyX2Jpb21hcmtlcnMucmFuay5kZiwgZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiLyIsVG9kYXksIi5jb3JyZWxhdGlvbl9jeXRva2luZXMudHh0IikpCgpgYGAKCmBgYHtyIFRhcmdldCB2cyBDeXRva2luZXMgaGVhdG1hcH0KIyBBZGQgY29ycmVsYXRpb24gY29lZmZpY2llbnRzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBhcmd1bWVudCBsYWIgPSBUUlVFCnAxIDwtIGdnY29ycnBsb3QoY29ycl9iaW9tYXJrZXJzLnJhbmssIAogICAgICAgICAgIG1ldGhvZCA9ICJzcXVhcmUiLCAKICAgICAgICAgICB0eXBlID0gImxvd2VyIiwKICAgICAgICAgICB0aXRsZSA9ICJDcm9zcyBiaW9tYXJrZXIgY29ycmVsYXRpb25zIiwgCiAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBUUlVFLCBsZWdlbmQudGl0bGUgPSBicXVvdGUoIlNwZWFybWFuJ3Mifml0YWxpYyhyaG8pKSwKICAgICAgICAgICBnZ3RoZW1lID0gZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCwgb3V0bGluZS5jb2xvciA9ICIjRkZGRkZGIiwKICAgICAgICAgICBzaG93LmRpYWcgPSBUUlVFLAogICAgICAgICAgIGhjLm9yZGVyID0gRkFMU0UsIAogICAgICAgICAgIGxhYiA9IEZBTFNFLAogICAgICAgICAgIGRpZ2l0cyA9IDMsCiAgICAgICAgICAgdGwuY2V4ID0gMTYsCiAgICAgICAgICAgIyB4bGFiID0gYygiTUNQMSIpLAogICAgICAgICAgICMgcC5tYXQgPSBjb3JyX2Jpb21hcmtlcnNfcC5yYW5rLCBzaWcubGV2ZWwgPSAwLjA1LAogICAgICAgICAgIGNvbG9ycyA9IGMoIiMxMjkwRDkiLCAiI0ZGRkZGRiIsICIjRTU1NzM4IikpCnAxCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi5jb3JyZWxhdGlvbl9jeXRva2luZXMucG5nIiksIHBsb3QgPSBsYXN0X3Bsb3QoKSkKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLmNvcnJlbGF0aW9uX2N5dG9raW5lcy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKcm0ocDEpCgpgYGAKCldoaWxlIHZpc3VhbGx5IGF0dHJhY3RpdmUgd2UgYXJlIG5vdCBuZWNlc3NhcmlseSBpbnRlcmVzdGVkIGluIHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBhbGwgdGhlIGN5dG9raW5lcywgcmF0aGVyIG9mIGByIFRSQUlUX09GX0lOVEVSRVNUYGAgd2l0aCBvdGhlciBjeXRva2luZXMgb25seS4KCiMjIyBITU9YMSAKCmBgYHtyIFRhcmdldCB2cyBDeXRva2luZXMgYmFycGxvdH0KdGVtcCA8LSBzdWJzZXQoY29ycl9iaW9tYXJrZXJzLnJhbmsuZGYsIEN5dG9raW5lX1ggPT0gIkhNT1gxIChSTkEpIiApCnRlbXAkcF9sb2cxMCA8LSAtbG9nMTAodGVtcCRwKQpwX3RocmVzaG9sZCA8LSAtbG9nMTAoMC4wNS9ucm93KHRlbXApKQpwX3RocmVzaG9sZAoKcDEgPC0gZ2dwdWJyOjpnZ2JhcnBsb3QodGVtcCwgCiAgICAgICAgICAgICAgICB4ID0gIkN5dG9raW5lWSIsIAogICAgICAgICAgICAgICAgeSA9ICJTcGVhcm1hblJobyIsCiAgICAgICAgICAgICAgICBmaWxsID0gIkN5dG9raW5lWSIsICAgICAgICAgICAgICAgIyBjaGFuZ2UgZmlsbCBjb2xvciBieSBjeWwKICAgICAgICAgICAgICAgICMgY29sb3IgPSAid2hpdGUiLCAgICAgICAgICAgICMgU2V0IGJhciBib3JkZXIgY29sb3JzIHRvIHdoaXRlCiAgICAgICAgICAgICAgICAjIHBhbGV0dGUgPSB1aXRob2ZfY29sb3IsICAgICAgICAgICAgIyBqY28gam91cm5hbCBjb2xvciBwYWxldHQuIHNlZSA/Z2dwYXIKICAgICAgICAgICAgICAgIHhsYWIgPSAiQ3l0b2tpbmUiLAogICAgICAgICAgICAgICAgc29ydC52YWwgPSAiZGVzYyIsICAgICAgICAgICMgU29ydCB0aGUgdmFsdWUgaW4gZHNjZW5kaW5nIG9yZGVyCiAgICAgICAgICAgICAgICBzb3J0LmJ5Lmdyb3VwcyA9IEZBTFNFLCAgICAgIyBEb24ndCBzb3J0IGluc2lkZSBlYWNoIGdyb3VwCiAgICAgICAgICAgICAgICB4LnRleHQuYW5nbGUgPSA0NSwgIyBSb3RhdGUgdmVydGljYWxseSB4IGF4aXMgdGV4dHMKICAgICAgICAgICAgICAgIGNleCA9IDEuMjUKICAgICAgICAgICAgICAgICkKZ2dwYXIocDEsIGxlZ2VuZCA9ICJib3R0b20iLCAKICAgICAgbGVnZW5kLnRpdGxlID0gIiIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICBsYWJzKHkgPSBleHByZXNzaW9uKHBhc3RlKCJTcGVhcm1hbidzIn5pdGFsaWMocmhvKSkpKQoKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDFfdnNfQ3l0b2tpbmVzLnBuZyIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxX3ZzX0N5dG9raW5lcy5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQpybShwMSkKCgpgYGAKCkFub3RoZXIgdmVyc2lvbiAtIHByb2JhYmx5IG5vdCBnb29kLiAKYGBge3J9CnRlbXAgPC0gc3Vic2V0KGNvcnJfYmlvbWFya2Vycy5yYW5rLmRmLCBDeXRva2luZV9YID09ICJITU9YMSAoUk5BKSIgKQp0ZW1wJHBfbG9nMTAgPC0gLWxvZzEwKHRlbXAkcCkKcF90aHJlc2hvbGQgPC0gLWxvZzEwKDAuMDUvbnJvdyh0ZW1wKSkKcF90aHJlc2hvbGQKcDEgPC0gZ2dkb3RjaGFydCh0ZW1wLCB4ID0gIkN5dG9raW5lWSIsIHkgPSAicF9sb2cxMCIsCiAgICAgICAgICAgY29sb3IgPSAiQ3l0b2tpbmVZIiwgI2ZpbGwgPSAiQ3l0b2tpbmVZIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIENvbG9yIGJ5IGdyb3VwcwogICAgICAgICAgICMgcGFsZXR0ZSA9IHVpdGhvZl9jb2xvciwgIyBDdXN0b20gY29sb3IgcGFsZXR0ZQogICAgICAgICAgIHhsYWIgPSAiQ3l0b2tpbmUiLAogICAgICAgICAgICMgeWxhYiA9IGV4cHJlc3Npb24obG9nWzEwXX4iKCJ+aXRhbGljKHApfiIpLXZhbHVlIiksCiAgICAgICAgICAgIyB5bGltID0gYygwLCA5KSwKICAgICAgICAgICBzb3J0aW5nID0gImRlc2NlbmRpbmciLCAgICAgICAgICAgICAgICAgICAgICAgIyBTb3J0IHZhbHVlIGluIGRlc2NlbmRpbmcgb3JkZXIKICAgICAgICAgICBhZGQgPSAic2VnbWVudHMiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBZGQgc2VnbWVudHMgZnJvbSB5ID0gMCB0byBkb3RzCiAgICAgICAgICAgcm90YXRlID0gRkFMU0UsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFJvdGF0ZSB2ZXJ0aWNhbGx5CiAgICAgICAgICAgIyBncm91cCA9ICJDeXRva2luZVkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBPcmRlciBieSBncm91cHMKICAgICAgICAgICBkb3Quc2l6ZSA9IDgsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBMYXJnZSBkb3Qgc2l6ZQogICAgICAgICAgIGxhYmVsID0gcm91bmQodGVtcCRTcGVhcm1hblJobywgZGlnaXRzID0gMyksICAgICAgICAgICAgICAgICAgICAgICAgIyBBZGQgbXBnIHZhbHVlcyBhcyBkb3QgbGFiZWxzCiAgICAgICAgICAgZm9udC5sYWJlbCA9IGxpc3QoY29sb3IgPSAid2hpdGUiLCBzaXplID0gNCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAwLjUpICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICkKZ2dwYXIocDEsIGxlZ2VuZCA9ICIiLCAKICAgICAgbGVnZW5kLnRpdGxlID0gIiIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICBsYWJzKHkgPSBleHByZXNzaW9uKGxvZ1sxMF1+Iigifml0YWxpYyhwKX4iKS12YWx1ZSIpKQoKZ2dzYXZlKGZpbGUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi4iLFRSQUlUX09GX0lOVEVSRVNULCIuSE1PWDFfdnNfQ3l0b2tpbmVzLmRvdGNoYXJ0LnBuZyIpLCBwbG90ID0gbGFzdF9wbG90KCkpCmdnc2F2ZShmaWxlID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsVG9kYXksIi5BRVJOQVNFLmNsaW4uIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxX3ZzX0N5dG9raW5lcy5kb3RjaGFydC5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKcm0odGVtcCwgcDEpCgpgYGAKCgojIyBUYXJnZXQgZXhwcmVzc2lvbiB2cy4gY3l0b2tpbmVzIHBsYXF1ZSBsZXZlbHMgYGxtKClgCgojIyMgTW9kZWwgMQoKSW4gdGhpcyBtb2RlbCB3ZSBjb3JyZWN0IGZvciBfQWdlXywgX0dlbmRlcl8sIGFuZCBfeWVhciBvZiBzdXJnZXJ5Xy4KCkhlcmUgd2UgdXNlIHRoZSBpbnZlcnNlLXJhbmsgbm9ybWFsaXplZCBkYXRhIC0gdmlzdWFsbHkgdGhpcyBpcyBtb3JlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLgoKQW5hbHlzaXMgb2YgcGxhcXVlIGN5dG9raW5lcyB0cmFpdHMgYXMgYSBmdW5jdGlvbiBvZiBwbGFxdWUgYHIgVFJBSVRfT0ZfSU5URVJFU1RgIGxldmVscy4KCmBgYHtyIENyb3NzU2VjOiBDeXRva2luZXMgLSBsaW5lYXIgcmVncmVzc2lvbiBNT0RFTDEgUkFOSywgcGFnZWQucHJpbnQ9VFJVRX0KCkdMTS5yZXN1bHRzIDwtIGRhdGEuZnJhbWUobWF0cml4KE5BLCBuY29sID0gMTUsIG5yb3cgPSAwKSkKY2F0KCJSdW5uaW5nIGxpbmVhciByZWdyZXNzaW9uLi4uXG4iKQpmb3IgKHByb3RlaW4gaW4gMTpsZW5ndGgoVFJBSVRTLlRBUkdFVC5SQU5LKSkgewogIFBST1RFSU4gPSBUUkFJVFMuVEFSR0VULlJBTktbcHJvdGVpbl0KICBjYXQocGFzdGUwKCJcbkFuYWx5c2lzIG9mICIsUFJPVEVJTiwiLlxuIikpCiAgZm9yICh0cmFpdCBpbiAxOmxlbmd0aChwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rKSkgewogICAgVFJBSVQgPSBwcm90ZWluc19vZl9pbnRlcmVzdF9yYW5rW3RyYWl0XQogICAgY2F0KHBhc3RlMCgiXG4tIHByb2Nlc3NpbmcgIixUUkFJVCwiXG5cbiIpKQogICAgY3VycmVudERGIDwtIGFzLmRhdGEuZnJhbWUoQUVSTkFTRS5jbGluICU+JQogICAgICBkcGx5cjo6c2VsZWN0KC4sIFBST1RFSU4sIFRSQUlULCBDT1ZBUklBVEVTX00xKSAlPiUKICAgICAgZmlsdGVyKGNvbXBsZXRlLmNhc2VzKC4pKSkgJT4lCiAgICAgIGZpbHRlcl9pZih+aXMubnVtZXJpYyguKSwgYWxsX3ZhcnMoIWlzLmluZmluaXRlKC4pKSkKICAgICMgZm9yIGRlYnVnCiAgICAjIHByaW50KERUOjpkYXRhdGFibGUoY3VycmVudERGKSkKICAgICMgcHJpbnQobnJvdyhjdXJyZW50REYpKQogICAgIyBwcmludChzdHIoY3VycmVudERGKSkKICAgICMjIyB1bml2YXJpYXRlCiAgICAjIGZpdCA8LSBsbShjdXJyZW50REZbLFBST1RFSU5dIH4gY3VycmVudERGWyxUUkFJVF0gKyBBZ2UgKyBHZW5kZXIgKyBPUmRhdGVfeWVhciwgZGF0YSA9IGN1cnJlbnRERikKICAgIGZpdCA8LSBsbShjdXJyZW50REZbLFBST1RFSU5dIH4gY3VycmVudERGWyxUUkFJVF0gKyBBZ2UgKyBHZW5kZXIgKyBPUmRhdGVfZXBvY2gsIGRhdGEgPSBjdXJyZW50REYpCiAgICBtb2RlbF9zdGVwIDwtIHN0ZXBBSUMoZml0LCBkaXJlY3Rpb24gPSAiYm90aCIsIHRyYWNlID0gRkFMU0UpCiAgICBwcmludChtb2RlbF9zdGVwKQogICAgcHJpbnQoc3VtbWFyeShmaXQpKQoKICAgIEdMTS5yZXN1bHRzLlRFTVAgPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5jb2wgPSAxNSwgbnJvdyA9IDApKQogICAgR0xNLnJlc3VsdHMuVEVNUFsxLF0gPSBHTE0uQ09OKGZpdCwgIkFFREIuQ0VBIiwgUFJPVEVJTiwgVFJBSVQsIHZlcmJvc2UgPSBUUlVFKQogICAgR0xNLnJlc3VsdHMgPSByYmluZChHTE0ucmVzdWx0cywgR0xNLnJlc3VsdHMuVEVNUCkKICB9Cn0KY2F0KCJFZGl0IHRoZSBjb2x1bW4gbmFtZXMuLi5cbiIpCmNvbG5hbWVzKEdMTS5yZXN1bHRzKSA9IGMoIkRhdGFzZXQiLCAiUHJlZGljdG9yIiwgIlRyYWl0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0YSIsICJzLmUubS4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICJPUiIsICJsb3c5NUNJIiwgInVwOTVDSSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlQtdmFsdWUiLCAiUC12YWx1ZSIsICJyXjIiLCAicl4yX2FkaiIsICJOIiwgIk1vZGVsX04iLCAiUGVyY19NaXNzIikKCmNhdCgiQ29ycmVjdCB0aGUgdmFyaWFibGUgdHlwZXMuLi5cbiIpCkdMTS5yZXN1bHRzJEJldGEgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRCZXRhKQpHTE0ucmVzdWx0cyRzLmUubS4gPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRzLmUubS4pCkdMTS5yZXN1bHRzJE9SIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkT1IpCkdMTS5yZXN1bHRzJGxvdzk1Q0kgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRsb3c5NUNJKQpHTE0ucmVzdWx0cyR1cDk1Q0kgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyR1cDk1Q0kpCkdMTS5yZXN1bHRzJGBULXZhbHVlYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBULXZhbHVlYCkKR0xNLnJlc3VsdHMkYFAtdmFsdWVgIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkYFAtdmFsdWVgKQpHTE0ucmVzdWx0cyRgcl4yYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGByXjJgKQpHTE0ucmVzdWx0cyRgcl4yX2FkamAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgcl4yX2FkamApCkdMTS5yZXN1bHRzJGBOYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBOYCkKR0xNLnJlc3VsdHMkYE1vZGVsX05gIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkYE1vZGVsX05gKQpHTE0ucmVzdWx0cyRgUGVyY19NaXNzYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBQZXJjX01pc3NgKQoKYGBgCgpgYGB7ciBDcm9zc1NlYzogQ3l0b2tpbmVzIC0gbGluZWFyIHJlZ3Jlc3Npb24gTU9ERUwxIFJBTksgV3JpdGluZ30KRFQ6OmRhdGF0YWJsZShHTE0ucmVzdWx0cykKCiMgU2F2ZSB0aGUgZGF0YQpjYXQoIldyaXRpbmcgcmVzdWx0cyB0byBFeGNlbC1maWxlLi4uXG4iKQojIyMgVW5pdmFyaWF0ZQpsaWJyYXJ5KG9wZW54bHN4KQp3cml0ZS54bHN4KEdMTS5yZXN1bHRzLAogICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8iLFRvZGF5LCIuQUVSTkFTRS5jbGluLkNvbi5VbmkuIixUUkFJVF9PRl9JTlRFUkVTVCwiX1BsYXF1ZS5DeXRva2luZXNfUGxhcXVlcy5SQU5LLk1PREVMMS54bHN4IiksCiAgICAgICAgICAgcm93Tm1lcyA9IEZBTFNFLCBjb2xOYW1lcyA9IFRSVUUsIHNoZWV0TmFtZSA9ICJDb24uVW5pLlBsYXF1ZVBoZW5vIikKIyBSZW1vdmluZyBpbnRlcm1lZGlhdGVzCmNhdCgiUmVtb3ZpbmcgaW50ZXJtZWRpYXRlIGZpbGVzLi4uXG4iKQpybShUUkFJVCwgdHJhaXQsIGN1cnJlbnRERiwgR0xNLnJlc3VsdHMsIEdMTS5yZXN1bHRzLlRFTVAsIGZpdCwgbW9kZWxfc3RlcCkKCgpgYGAKCgoKIyMjIE1vZGVsIDIKCkluIHRoaXMgbW9kZWwgd2UgY29ycmVjdCBmb3IgX0FnZV8sIF9HZW5kZXJfLCBfeWVhciBvZiBzdXJnZXJ5XywgX0h5cGVydGVuc2lvbiBzdGF0dXNfLCBfRGlhYmV0ZXMgc3RhdHVzXywgX2N1cnJlbnQgc21va2VyIHN0YXR1c18sIF9saXBpZC1sb3dlcmluZyBkcnVncyAoTExEcylfLCBfYW50aXBsYXRlbGV0IG1lZGljYXRpb25fLCBfZUdGUiAoTURSRClfLCBfQk1JXywgX01lZEh4X0NWRF8gKGNvbWJpbmF0aW9uIG9mIF9DQUQgaGlzdG9yeV8sIF9zdHJva2UgaGlzdG9yeV8sIGFuZCBfcGVyaXBoZXJhbCBpbnRlcnZlbnRpb25zXyksIGFuZCBfc3Rlbm9zaXNfLgoKSGVyZSB3ZSB1c2UgdGhlIGludmVyc2UtcmFuayBub3JtYWxpemVkIGRhdGEgLSB2aXN1YWxseSB0aGlzIGlzIG1vcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuCgpBbmFseXNpcyBvZiBwbGFxdWUgY3l0b2tpbmVzIGFzIGEgZnVuY3Rpb24gb2YgcGxhcXVlIGByIFRSQUlUX09GX0lOVEVSRVNUYCBsZXZlbHMuCgpgYGB7ciBDcm9zc1NlYzogQ3l0b2tpbmVzIC0gbGluZWFyIHJlZ3Jlc3Npb24gTU9ERUwyIFJBTkssIHBhZ2VkLnByaW50PVRSVUV9CgpHTE0ucmVzdWx0cyA8LSBkYXRhLmZyYW1lKG1hdHJpeChOQSwgbmNvbCA9IDE1LCBucm93ID0gMCkpCmNhdCgiUnVubmluZyBsaW5lYXIgcmVncmVzc2lvbi4uLlxuIikKZm9yIChwcm90ZWluIGluIDE6bGVuZ3RoKFRSQUlUUy5UQVJHRVQuUkFOSykpIHsKICBQUk9URUlOID0gVFJBSVRTLlRBUkdFVC5SQU5LW3Byb3RlaW5dCiAgY2F0KHBhc3RlMCgiXG5BbmFseXNpcyBvZiAiLFBST1RFSU4sIi5cbiIpKQogIGZvciAodHJhaXQgaW4gMTpsZW5ndGgocHJvdGVpbnNfb2ZfaW50ZXJlc3RfcmFuaykpIHsKICAgIFRSQUlUID0gcHJvdGVpbnNfb2ZfaW50ZXJlc3RfcmFua1t0cmFpdF0KICAgIGNhdChwYXN0ZTAoIlxuLSBwcm9jZXNzaW5nICIsVFJBSVQsIlxuXG4iKSkKICAgIGN1cnJlbnRERiA8LSBhcy5kYXRhLmZyYW1lKEFFUk5BU0UuY2xpbiAlPiUKICAgICAgZHBseXI6OnNlbGVjdCguLCBQUk9URUlOLCBUUkFJVCwgQ09WQVJJQVRFU19NMikgJT4lCiAgICAgIGZpbHRlcihjb21wbGV0ZS5jYXNlcyguKSkpICU+JQogICAgICBmaWx0ZXJfaWYofmlzLm51bWVyaWMoLiksIGFsbF92YXJzKCFpcy5pbmZpbml0ZSguKSkpCiAgICAjIGZvciBkZWJ1ZwogICAgIyBwcmludChEVDo6ZGF0YXRhYmxlKGN1cnJlbnRERikpCiAgICAjIHByaW50KG5yb3coY3VycmVudERGKSkKICAgICMgcHJpbnQoc3RyKGN1cnJlbnRERikpCiAgICAjIyMgdW5pdmFyaWF0ZQogICAgIyBmaXQgPC0gbG0oY3VycmVudERGWyxQUk9URUlOXSB+IGN1cnJlbnRERlssVFJBSVRdICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX3llYXIgKyAKICAgICMgICAgICAgICAgICAgSHlwZXJ0ZW5zaW9uLmNvbXBvc2l0ZSArIERpYWJldGVzU3RhdHVzICsgU21va2VyU3RhdHVzICsgCiAgICAjICAgICAgICAgICAgIE1lZC5TdGF0aW4uTExEICsgTWVkLmFsbC5hbnRpcGxhdGVsZXQgKyBHRlJfTURSRCArIEJNSSArIAogICAgIyAgICAgICAgICAgICBNZWRIeF9DVkQgKyBzdGVub3NlLCAKICAgICMgICAgICAgICAgIGRhdGEgPSBjdXJyZW50REYpCiAgICBmaXQgPC0gbG0oY3VycmVudERGWyxQUk9URUlOXSB+IGN1cnJlbnRERlssVFJBSVRdICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX2Vwb2NoICsgCiAgICAgICAgICAgICAgICBIeXBlcnRlbnNpb24uY29tcG9zaXRlICsgRGlhYmV0ZXNTdGF0dXMgKyBTbW9rZXJTdGF0dXMgKyAKICAgICAgICAgICAgICAgIE1lZC5TdGF0aW4uTExEICsgTWVkLmFsbC5hbnRpcGxhdGVsZXQgKyBHRlJfTURSRCArIEJNSSArIAogICAgICAgICAgICAgICAgTWVkSHhfQ1ZEICsgc3Rlbm9zZSwgCiAgICAgICAgICAgICAgZGF0YSA9IGN1cnJlbnRERikKICAgIG1vZGVsX3N0ZXAgPC0gc3RlcEFJQyhmaXQsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSBGQUxTRSkKICAgIHByaW50KG1vZGVsX3N0ZXApCiAgICBwcmludChzdW1tYXJ5KGZpdCkpCiAgICAKICAgIEdMTS5yZXN1bHRzLlRFTVAgPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5jb2wgPSAxNSwgbnJvdyA9IDApKQogICAgR0xNLnJlc3VsdHMuVEVNUFsxLF0gPSBHTE0uQ09OKGZpdCwgIkFFREIuQ0VBIiwgUFJPVEVJTiwgVFJBSVQsIHZlcmJvc2UgPSBUUlVFKQogICAgR0xNLnJlc3VsdHMgPSByYmluZChHTE0ucmVzdWx0cywgR0xNLnJlc3VsdHMuVEVNUCkKICB9Cn0KY2F0KCJFZGl0IHRoZSBjb2x1bW4gbmFtZXMuLi5cbiIpCmNvbG5hbWVzKEdMTS5yZXN1bHRzKSA9IGMoIkRhdGFzZXQiLCAiUHJlZGljdG9yIiwgIlRyYWl0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0YSIsICJzLmUubS4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICJPUiIsICJsb3c5NUNJIiwgInVwOTVDSSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlQtdmFsdWUiLCAiUC12YWx1ZSIsICJyXjIiLCAicl4yX2FkaiIsICJOIiwgIk1vZGVsX04iLCAiUGVyY19NaXNzIikKCmNhdCgiQ29ycmVjdCB0aGUgdmFyaWFibGUgdHlwZXMuLi5cbiIpCkdMTS5yZXN1bHRzJEJldGEgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRCZXRhKQpHTE0ucmVzdWx0cyRzLmUubS4gPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRzLmUubS4pCkdMTS5yZXN1bHRzJE9SIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkT1IpCkdMTS5yZXN1bHRzJGxvdzk1Q0kgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRsb3c5NUNJKQpHTE0ucmVzdWx0cyR1cDk1Q0kgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyR1cDk1Q0kpCkdMTS5yZXN1bHRzJGBULXZhbHVlYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBULXZhbHVlYCkKR0xNLnJlc3VsdHMkYFAtdmFsdWVgIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkYFAtdmFsdWVgKQpHTE0ucmVzdWx0cyRgcl4yYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGByXjJgKQpHTE0ucmVzdWx0cyRgcl4yX2FkamAgPC0gYXMubnVtZXJpYyhHTE0ucmVzdWx0cyRgcl4yX2FkamApCkdMTS5yZXN1bHRzJGBOYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBOYCkKR0xNLnJlc3VsdHMkYE1vZGVsX05gIDwtIGFzLm51bWVyaWMoR0xNLnJlc3VsdHMkYE1vZGVsX05gKQpHTE0ucmVzdWx0cyRgUGVyY19NaXNzYCA8LSBhcy5udW1lcmljKEdMTS5yZXN1bHRzJGBQZXJjX01pc3NgKQoKYGBgCgpgYGB7ciBDcm9zc1NlYzogQ3l0b2tpbmVzIC0gbGluZWFyIHJlZ3Jlc3Npb24gTU9ERUwyIFJBTkssIHdyaXRpbmd9CkRUOjpkYXRhdGFibGUoR0xNLnJlc3VsdHMpCgojIFNhdmUgdGhlIGRhdGEKY2F0KCJXcml0aW5nIHJlc3VsdHMgdG8gRXhjZWwtZmlsZS4uLlxuIikKIyMjIFVuaXZhcmlhdGUKbGlicmFyeShvcGVueGxzeCkKd3JpdGUueGxzeChHTE0ucmVzdWx0cywKICAgICAgICAgICBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvIixUb2RheSwiLkFFUk5BU0UuY2xpbi5Db24uTXVsdGkuIixUUkFJVF9PRl9JTlRFUkVTVCwiX1BsYXF1ZS5DeXRva2luZXNfUGxhcXVlcy5SQU5LLk1PREVMMi54bHN4IiksCiAgICAgICAgICAgcm93TmFtZXMgPSBGQUxTRSwgY29sTmFtZXMgPSBUUlVFLCBzaGVldE5hbWUgPSAiQ29uLk11bHRpLlBsYXF1ZVBoZW5vIikKIyBSZW1vdmluZyBpbnRlcm1lZGlhdGVzCmNhdCgiUmVtb3ZpbmcgaW50ZXJtZWRpYXRlIGZpbGVzLi4uXG4iKQpybShUUkFJVCwgdHJhaXQsIGN1cnJlbnRERiwgR0xNLnJlc3VsdHMsIEdMTS5yZXN1bHRzLlRFTVAsIGZpdCwgbW9kZWxfc3RlcCkKCgpgYGAKCiMjIFRhcmdldCBleHByZXNzaW9uIHZzLiB2dWxuZXJhYmlsaXR5IGluZGV4CgoKSGVyZSB3ZSBwbG90IHRoZSBsZXZlbHMgb2YgaW52ZXJzZS1yYW5rIG5vcm1hbCB0cmFuc2Zvcm1lZCBgciBUUkFJVF9PRl9JTlRFUkVTVGAgcGxhcXVlIGxldmVscyBmcm9tIGV4cGVyaW1lbnQgMSBhbmQgMiB0byB0aGUgYFBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4YC4gCgpgYGB7ciBGaXggT1J5ZWFyR3JvdXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc2psYWJlbGxlZCkKCkFFUk5BU0UuY2xpbiR5ZWFydGVtcCA8LSBhcy5udW1lcmljKHllYXIoQUVSTkFTRS5jbGluJGRhdGVvaykpCgphdHRhY2goQUVSTkFTRS5jbGluKQoKQUVSTkFTRS5jbGluWywiT1J5ZWFyR3JvdXAiXSA8LSBOQQpBRVJOQVNFLmNsaW4kT1J5ZWFyR3JvdXBbeWVhcnRlbXAgPD0gMjAwN10gPC0gIjwgMjAwNyIKQUVSTkFTRS5jbGluJE9SeWVhckdyb3VwW3llYXJ0ZW1wID4gMjAwN10gPC0gIj4gMjAwNyIKZGV0YWNoKEFFUk5BU0UuY2xpbikKCnRhYmxlKEFFUk5BU0UuY2xpbiRPUnllYXJHcm91cCwgQUVSTkFTRS5jbGluJE9SZGF0ZV95ZWFyKQpgYGAKCiMjIyBWaXN1YWxpc2F0aW9ucwoKIyMjIyBITU9YMQoKYGBge3IgcGVyIFBsYXF1ZVZ1bG5lcmFiaWxpdHlJbmRleH0KIyBHbG9iYWwgdGVzdAoKY29tcGFyZV9tZWFucyhITU9YMSB+IFBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4LCAgZGF0YSA9IEFFUk5BU0UuY2xpbiwgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCnAxIDwtIGdncHVicjo6Z2dib3hwbG90KEFFUk5BU0UuY2xpbiwgCiAgICAgICAgICAgICAgICAgIHggPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICB5ID0gIkhNT1gxIiwgCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkhNT1gxIChub3JtYWxpemVkIGV4cHJlc3Npb24pXG4iLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHAxLCBsZWdlbmQgPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuUGxhcXVlVnVsbmVyYWJpbGl0eUluZGV4LnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgsIGdyb3VwLmJ5ID0gIkdlbmRlciIsIGRhdGEgPSBBRVJOQVNFLmNsaW4sIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwMiA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4sIAogICAgICAgICAgICAgICAgICB4ID0gIlBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4IiwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IGJ5IGdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gR2VuZGVyKSwgbGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHAyLCBsZWdlbmQgPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuUGxhcXVlVnVsbmVyYWJpbGl0eUluZGV4LmJ5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgoKCgpjb21wYXJlX21lYW5zKEhNT1gxIH4gUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgsIGRhdGEgPSBBRVJOQVNFLmNsaW4sIG1ldGhvZCA9ICJrcnVza2FsLnRlc3QiKQpwNSA8LSBnZ3B1YnI6OmdnYm94cGxvdChBRVJOQVNFLmNsaW4sIAogICAgICAgICAgICAgICAgICB4ID0gIlBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4IiwKICAgICAgICAgICAgICAgICAgeSA9ICJITU9YMSIsIAogICAgICAgICAgICAgICAgICB4bGFiID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJITU9YMSAobm9ybWFsaXplZCBleHByZXNzaW9uKVxuIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiLAogICAgICAgICAgICAgICAgICBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIGZhY2V0LmJ5ID0gIk9SeWVhckdyb3VwIiwKICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5mb3JtYXQiLCAgbWV0aG9kID0gImtydXNrYWwudGVzdCIpCmdncGFyKHA1LCBsZWdlbmQgPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gIlBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IikKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLiIsVFJBSVRfT0ZfSU5URVJFU1QsIi5ITU9YMS5wbGFxdWUuUGxhcXVlVnVsbmVyYWJpbGl0eUluZGV4X0ZhY2V0X2J5WWVhci5wZGYiKSwgcGxvdCA9IGxhc3RfcGxvdCgpKQoKY29tcGFyZV9tZWFucyhITU9YMSB+IFBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4LCBncm91cC5ieSA9ICJHZW5kZXIiLCBkYXRhID0gQUVSTkFTRS5jbGluLCBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKcDYgPC0gZ2dwdWJyOjpnZ2JveHBsb3QoQUVSTkFTRS5jbGluLCAKICAgICAgICAgICAgICAgICAgeCA9ICJQbGFxdWVfVnVsbmVyYWJpbGl0eV9JbmRleCIsCiAgICAgICAgICAgICAgICAgIHkgPSAiSE1PWDEiLCAKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJQbGFxdWUgdnVsbmVyYWJpbGl0eSBpbmRleCIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiSE1PWDEgKG5vcm1hbGl6ZWQgZXhwcmVzc2lvbilcbiIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gIkdlbmRlciIsCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjRDUyNjdCIiwgIiMxMjkwRDkiKSwKICAgICAgICAgICAgICAgICAgZmFjZXQuYnkgPSAiT1J5ZWFyR3JvdXAiLAogICAgICAgICAgICAgICAgICBhZGQgPSAiaml0dGVyIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBHZW5kZXIpLCBsYWJlbCA9ICJwLmZvcm1hdCIsICBtZXRob2QgPSAia3J1c2thbC50ZXN0IikKZ2dwYXIocDYsIGxlZ2VuZCA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSAiUGxhcXVlIHZ1bG5lcmFiaWxpdHkgaW5kZXgiKQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoUExPVF9sb2MsICIvIiwgVG9kYXksICIuIixUUkFJVF9PRl9JTlRFUkVTVCwiLkhNT1gxLnBsYXF1ZS5QbGFxdWVWdWxuZXJhYmlsaXR5SW5kZXhfRmFjZXRfYnlZZWFyLmJ5R2VuZGVyLnBkZiIpLCBwbG90ID0gbGFzdF9wbG90KCkpCgpgYGAKCgojIyMgTW9kZWwgMQoKSW4gdGhpcyBtb2RlbCB3ZSBjb3JyZWN0IGZvciBfQWdlXywgX0dlbmRlcl8sIGFuZCBfeWVhciBvZiBzdXJnZXJ5Xy4KCkhlcmUgd2UgdXNlIHRoZSBpbnZlcnNlLXJhbmsgbm9ybWFsaXplZCBkYXRhIC0gdmlzdWFsbHkgdGhpcyBpcyBtb3JlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLgoKQW5hbHlzaXMgb2YgdGhlIHBsYXF1ZSB2dWxuZXJhYmlsaXR5IGluZGV4IGFzIGEgZnVuY3Rpb24gb2YgcGxhcXVlIGByIFRSQUlUX09GX0lOVEVSRVNUYCBsZXZlbHMuCgpgYGB7ciBDcm9zc1NlYzogUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXggLSBvcmRpbmFsIHJlZ3Jlc3Npb24gTU9ERUwxIFJBTkssIHBhZ2VkLnByaW50PVRSVUV9ClRSQUlUUy5UQVJHRVQuUkFOSy5leHRyYSA9IGMoIkhNT1gxIikKCkdMTS5yZXN1bHRzIDwtIGRhdGEuZnJhbWUobWF0cml4KE5BLCBuY29sID0gMTYsIG5yb3cgPSAwKSkKZm9yIChwcm90ZWluIGluIDE6bGVuZ3RoKFRSQUlUUy5UQVJHRVQuUkFOSy5leHRyYSkpIHsKICBQUk9URUlOID0gVFJBSVRTLlRBUkdFVC5SQU5LLmV4dHJhW3Byb3RlaW5dCiAgY2F0KHBhc3RlMCgiXG5BbmFseXNpcyBvZiAiLFBST1RFSU4sIi5cbiIpKQogIFRSQUlUID0gIlBsYXF1ZV9WdWxuZXJhYmlsaXR5X0luZGV4IgogICAgY2F0KHBhc3RlMCgiXG4tIHByb2Nlc3NpbmcgIixUUkFJVCwiXG5cbiIpKQogICAgY3VycmVudERGIDwtIGFzLmRhdGEuZnJhbWUoQUVSTkFTRS5jbGluICU+JQogICAgICBkcGx5cjo6c2VsZWN0KC4sIFBST1RFSU4sIFRSQUlULCBDT1ZBUklBVEVTX00xKSAlPiUKICAgICAgZmlsdGVyKGNvbXBsZXRlLmNhc2VzKC4pKSkgJT4lCiAgICAgIGZpbHRlcl9pZih+aXMubnVtZXJpYyguKSwgYWxsX3ZhcnMoIWlzLmluZmluaXRlKC4pKSkgJT4lCiAgICAgIGRyb3BsZXZlbHMoLikKICAgIAogICAgIyBmaXggbnVtZXJpYyBPUiB5ZWFyCiAgICAjIGN1cnJlbnRERiRPUmRhdGVfeWVhciA8LSBhcy5udW1lcmljKGN1cnJlbnRERiRPUmRhdGVfeWVhcikKICAgIAogICAgIyBmb3IgZGVidWcKICAgICMgcHJpbnQoRFQ6OmRhdGF0YWJsZShjdXJyZW50REYpKQogICAgIyBwcmludChucm93KGN1cnJlbnRERikpCiAgICAjIHByaW50KHN0cihjdXJyZW50REYpKQogICAgIyBwcmludChjbGFzcyhjdXJyZW50REZbLFRSQUlUXSkpCiAgICAjIHRhYmxlKGN1cnJlbnRERiRPUmRhdGVfeWVhcikKICAgICMjIyB1bml2YXJpYXRlCiAgICAgIyArIEh5cGVydGVuc2lvbi5jb21wb3NpdGUgKyBEaWFiZXRlc1N0YXR1cyArIFNtb2tlckN1cnJlbnQgKyAKICAgICAjICAgICAgICAgICAgTWVkLlN0YXRpbi5MTEQgKyBNZWQuYWxsLmFudGlwbGF0ZWxldCArIEdGUl9NRFJEICsgQk1JICsgCiAgICAgIyAgICAgICAgICAgIENBRF9oaXN0b3J5ICsgU3Ryb2tlX2hpc3RvcnkgKyBQZXJpcGhlcmFsLmludGVydiArIHN0ZW5vc2UKICAgICMgZml0IDwtIHBvbHIoY3VycmVudERGWyxUUkFJVF0gfiBjdXJyZW50REZbLFBST1RFSU5dICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX3llYXIsIAogICAgIyAgICAgICAgICAgZGF0YSAgPSAgY3VycmVudERGLCAKICAgICMgICAgICAgICAgIEhlc3MgPSBUUlVFKQogICAgZml0IDwtIHBvbHIoY3VycmVudERGWyxUUkFJVF0gfiBjdXJyZW50REZbLFBST1RFSU5dICsgQWdlICsgR2VuZGVyICsgT1JkYXRlX2Vwb2NoLCAKICAgICAgICAgICAgICBkYXRhICA9ICBjdXJyZW50REYsIAogICAgICAgICAgICAgIEhlc3MgPSBUUlVFKQogICAgcHJpbnQoc3VtbWFyeShmaXQpKQogICAgCiAgICAjIyBzdG9yZSB0YWJsZQogICAgKGN0YWJsZSA8LSBjb2VmKHN1bW1hcnkoZml0KSkpCgogICAgIyMgY2FsY3VsYXRlIGFuZCBzdG9yZSBwIHZhbHVlcwogICAgcCA8LSBwbm9ybShhYnMoY3RhYmxlWywgInQgdmFsdWUiXSksIGxvd2VyLnRhaWwgPSBGQUxTRSkgKiAyCiAgICAKICAgICMjIGNvbWJpbmVkIHRhYmxlCiAgICBwcmludCgoY3RhYmxlIDwtIGNiaW5kKGN0YWJsZSwgInAgdmFsdWUiID0gcCkpKQogIH0KCgpgYGAKCiMjIyBNb2RlbCAyCgpJbiB0aGlzIG1vZGVsIHdlIGNvcnJlY3QgZm9yIF9BZ2VfLCBfR2VuZGVyXywgX0h5cGVydGVuc2lvbiBzdGF0dXNfLCBfRGlhYmV0ZXMgc3RhdHVzXywgX2N1cnJlbnQgc21va2VyIHN0YXR1c18sIF9saXBpZC1sb3dlcmluZyBkcnVncyAoTExEcylfLCBfYW50aXBsYXRlbGV0IG1lZGljYXRpb25fLCBfZUdGUiAoTURSRClfLCBfQk1JXywgX01lZEh4X0NWRF8gKGNvbWJpbmF0aW9uIG9mIF9DQUQgaGlzdG9yeV8sIF9zdHJva2UgaGlzdG9yeV8sIGFuZCBfcGVyaXBoZXJhbCBpbnRlcnZlbnRpb25zXyksIGFuZCBfc3Rlbm9zaXMuXy4KCgpgYGB7ciBDcm9zc1NlYzogUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXggLSBvcmRpbmFsIHJlZ3Jlc3Npb24gTU9ERUwyIFJBTkssIHBhZ2VkLnByaW50PVRSVUV9Cgpmb3IgKHByb3RlaW4gaW4gMTpsZW5ndGgoVFJBSVRTLlRBUkdFVC5SQU5LLmV4dHJhKSkgewogIFBST1RFSU4gPSBUUkFJVFMuVEFSR0VULlJBTksuZXh0cmFbcHJvdGVpbl0KICBjYXQocGFzdGUwKCJcbkFuYWx5c2lzIG9mICIsUFJPVEVJTiwiLlxuIikpCiAgVFJBSVQgPSAiUGxhcXVlX1Z1bG5lcmFiaWxpdHlfSW5kZXgiCiAgICBjYXQocGFzdGUwKCJcbi0gcHJvY2Vzc2luZyAiLFRSQUlULCJcblxuIikpCiAgICBjdXJyZW50REYgPC0gYXMuZGF0YS5mcmFtZShBRVJOQVNFLmNsaW4gJT4lCiAgICAgIGRwbHlyOjpzZWxlY3QoLiwgUFJPVEVJTiwgVFJBSVQsIENPVkFSSUFURVNfTTIpICU+JQogICAgICBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpKSAlPiUKICAgICAgZmlsdGVyX2lmKH5pcy5udW1lcmljKC4pLCBhbGxfdmFycyghaXMuaW5maW5pdGUoLikpKSAlPiUKICAgICAgZHJvcGxldmVscyguKQogICAgCiAgICAjIGZpeCBudW1lcmljIE9SIHllYXIKICAgICMgY3VycmVudERGJE9SZGF0ZV95ZWFyIDwtIGFzLm51bWVyaWMoY3VycmVudERGJE9SZGF0ZV95ZWFyKQogICAgCiAgICAjIGZvciBkZWJ1ZwogICAgIyBwcmludChEVDo6ZGF0YXRhYmxlKGN1cnJlbnRERikpCiAgICAjIHByaW50KG5yb3coY3VycmVudERGKSkKICAgICMgcHJpbnQoc3RyKGN1cnJlbnRERikpCiAgICAjIHByaW50KGNsYXNzKGN1cnJlbnRERlssVFJBSVRdKSkKICAgICMjIyB1bml2YXJpYXRlCgogICAgIyBmaXQgPC0gcG9scihhcy5mYWN0b3IoY3VycmVudERGWyxUUkFJVF0pIH4gY3VycmVudERGWyxQUk9URUlOXSArIEFnZSArIEdlbmRlciArIE9SZGF0ZV95ZWFyICsgSHlwZXJ0ZW5zaW9uLmNvbXBvc2l0ZSArIERpYWJldGVzU3RhdHVzICsgU21va2VyU3RhdHVzICsgTWVkLlN0YXRpbi5MTEQgKyBNZWQuYWxsLmFudGlwbGF0ZWxldCArIEdGUl9NRFJEICsgQk1JICsgTWVkSHhfQ1ZEICsgc3Rlbm9zZSwKICAgICMgICAgICAgICAgIGRhdGEgID0gIGN1cnJlbnRERiwKICAgICMgICAgICAgICAgIEhlc3MgPSBUUlVFKQogICAgCiAgICBmaXQgPC0gcG9scihhcy5mYWN0b3IoY3VycmVudERGWyxUUkFJVF0pIH4gY3VycmVudERGWyxQUk9URUlOXSArIEFnZSArIEdlbmRlciArIE9SZGF0ZV9lcG9jaCArIEh5cGVydGVuc2lvbi5jb21wb3NpdGUgKyBEaWFiZXRlc1N0YXR1cyArIFNtb2tlclN0YXR1cyArIE1lZC5TdGF0aW4uTExEICsgTWVkLmFsbC5hbnRpcGxhdGVsZXQgKyBHRlJfTURSRCArIEJNSSArIE1lZEh4X0NWRCArIHN0ZW5vc2UsCiAgICAgICAgICAgICAgZGF0YSAgPSAgY3VycmVudERGLAogICAgICAgICAgICAgIEhlc3MgPSBUUlVFKQogICAgCiAgICBwcmludChzdW1tYXJ5KGZpdCkpCiAgICAKICAgICMjIHN0b3JlIHRhYmxlCiAgICAoY3RhYmxlIDwtIGNvZWYoc3VtbWFyeShmaXQpKSkKCiAgICAjIyBjYWxjdWxhdGUgYW5kIHN0b3JlIHAgdmFsdWVzCiAgICBwIDwtIHBub3JtKGFicyhjdGFibGVbLCAidCB2YWx1ZSJdKSwgbG93ZXIudGFpbCA9IEZBTFNFKSAqIDIKICAgIAogICAgIyMgY29tYmluZWQgdGFibGUKICAgIHByaW50KChjdGFibGUgPC0gY2JpbmQoY3RhYmxlLCAicCB2YWx1ZSIgPSBwKSkpCiAgfQoKYGBgCgoKIyBTZXNzaW9uIGluZm9ybWF0aW9uCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKICAgIFZlcnNpb246ICAgICAgdjEuMS4xCiAgICBMYXN0IHVwZGF0ZTogIDIwMjQtMDEtMDkKICAgIFdyaXR0ZW4gYnk6ICAgU2FuZGVyIFcuIHZhbiBkZXIgTGFhbiAocy53LnZhbmRlcmxhYW4tMlthdF11bWN1dHJlY2h0Lm5sKS4KICAgIERlc2NyaXB0aW9uOiAgU2NyaXB0IHRvIGFuYWx5c2UgVGFyZ2V0cyBmcm9tIHRoZSBBdGhlci1FeHByZXNzIEJpb2JhbmsgU3R1ZHkuCiAgICBNaW5pbXVtIHJlcXVpcmVtZW50czogUiB2ZXJzaW9uIDMuNS4yICgyMDE4LTEyLTIwKSAtLSAnRWdnc2hlbGwgSWdsb28nLCBtYWNPUyBNb2phdmUgKDEwLjE0LjIpLgogICAgCiAgICAqKk1vU0NvVyBUby1EbyBMaXN0KioKICAgIFRoZSB0aGluZ3Mgd2UgTXVzdCwgU2hvdWxkLCBDb3VsZCwgYW5kIFdvdWxkIGhhdmUgZ2l2ZW4gdGhlIHRpbWUgd2UgaGF2ZS4KICAgIF9NXwoKICAgIF9TXwoKICAgIF9DXwoKICAgIF9XXwoKICAgICoqQ2hhbmdlcyBsb2cqKgogICAgKiB2MS4xLjEgVGV4dHVhbCBmaXhlcy4KICAgICogdjEuMS4wIFVwZGF0ZSB0byBzdHVkeSBkYXRhYmFzZTsgdXBkYXRlIHRvIGJ1bGsgUk5Bc2VxIGRhdGEgKGRlZXBlciBzZXF1ZW5jZWQpLgogICAgKiB2MS4wLjEgRml4IHRvIHRoZSBzdGFydCBvZiB0aGlzIG5vdGVib29rLgogICAgKiB2MS4wLjAgSW5pdGFsIHZlcnNpb24uCiAgICAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpgYGB7ciBldmFsID0gVFJVRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgU2F2aW5nIGVudmlyb25tZW50CmBgYHtyIFNhdmluZ30Kc2F2ZS5pbWFnZShwYXN0ZTAoUFJPSkVDVF9sb2MsICIvIixUb2RheSwiLiIsUFJPSkVDVE5BTUUsIi5idWxrUk5Bc2VxLmFkZGl0aW9uYWxfZmlndXJlcy5SRGF0YSIpKQpgYGAKCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCA8c3VwPsKpIDE5NzktMjAyNCBTYW5kZXIgVy4gdmFuIGRlciBMYWFuIHwgcy53LnZhbmRlcmxhYW5bYXRdZ21haWxbZG90XWNvbSB8IFt2YW5kZXJsYWFuYW5kLnNjaWVuY2VdKGh0dHBzOi8vdmFuZGVybGFhbmFuZC5zY2llbmNlKS48L3N1cD4gfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCg==